mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
Backend plugin system (#2874)
* Add backend plugin changes * Fix totp backend plugin tests * Fix logical/plugin InvalidateKey test * Fix plugin catalog CRUD test, fix NoopBackend * Clean up commented code block * Fix system backend mount test * Set plugin_name to omitempty, fix handleMountTable config parsing * Clean up comments, keep shim connections alive until cleanup * Include pluginClient, disallow LookupPlugin call from within a plugin * Add wrapper around backendPluginClient for proper cleanup * Add logger shim tests * Add logger, storage, and system shim tests * Use pointer receivers for system view shim * Use plugin name if no path is provided on mount * Enable plugins for auth backends * Add backend type attribute, move builtin/plugin/package * Fix merge conflict * Fix missing plugin name in mount config * Add integration tests on enabling auth backend plugins * Remove dependency cycle on mock-plugin * Add passthrough backend plugin, use logical.BackendType to determine lease generation * Remove vault package dependency on passthrough package * Add basic impl test for passthrough plugin * Incorporate feedback; set b.backend after shims creation on backendPluginServer * Fix totp plugin test * Add plugin backends docs * Fix tests * Fix builtin/plugin tests * Remove flatten from PluginRunner fields * Move mock plugin to logical/plugin, remove totp and passthrough plugins * Move pluginMap into newPluginClient * Do not create storage RPC connection on HandleRequest and HandleExistenceCheck * Change shim logger's Fatal to no-op * Change BackendType to uint32, match UX backend types * Change framework.Backend Setup signature * Add Setup func to logical.Backend interface * Move OptionallyEnableMlock call into plugin.Serve, update docs and comments * Remove commented var in plugin package * RegisterLicense on logical.Backend interface (#3017) * Add RegisterLicense to logical.Backend interface * Update RegisterLicense to use callback func on framework.Backend * Refactor framework.Backend.RegisterLicense * plugin: Prevent plugin.SystemViewClient.ResponseWrapData from getting JWTs * plugin: Revert BackendType to remove TypePassthrough and related references * Fix typo in plugin backends docs
This commit is contained in:
committed by
GitHub
parent
987616895d
commit
2b0f80b981
@@ -85,6 +85,7 @@ type EnableAuthOptions struct {
|
|||||||
Type string `json:"type" structs:"type"`
|
Type string `json:"type" structs:"type"`
|
||||||
Description string `json:"description" structs:"description"`
|
Description string `json:"description" structs:"description"`
|
||||||
Local bool `json:"local" structs:"local"`
|
Local bool `json:"local" structs:"local"`
|
||||||
|
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthMount struct {
|
type AuthMount struct {
|
||||||
@@ -96,6 +97,7 @@ type AuthMount struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AuthConfigOutput struct {
|
type AuthConfigOutput struct {
|
||||||
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||||
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||||
|
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ type MountConfigInput struct {
|
|||||||
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||||
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||||
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
||||||
|
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MountOutput struct {
|
type MountOutput struct {
|
||||||
@@ -141,7 +142,8 @@ type MountOutput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MountConfigOutput struct {
|
type MountConfigOutput struct {
|
||||||
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||||
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||||
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
||||||
|
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,13 @@ func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return b.Backend.Setup(conf)
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend(conf *logical.BackendConfig) (backend, error) {
|
func Backend(conf *logical.BackendConfig) (*backend, error) {
|
||||||
var b backend
|
var b backend
|
||||||
b.MapAppId = &framework.PolicyMap{
|
b.MapAppId = &framework.PolicyMap{
|
||||||
PathMap: framework.PathMap{
|
PathMap: framework.PathMap{
|
||||||
@@ -78,7 +81,7 @@ func Backend(conf *logical.BackendConfig) (backend, error) {
|
|||||||
b.MapAppId.SaltFunc = b.Salt
|
b.MapAppId.SaltFunc = b.Salt
|
||||||
b.MapUserId.SaltFunc = b.Salt
|
b.MapUserId.SaltFunc = b.Salt
|
||||||
|
|
||||||
return b, nil
|
return &b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type backend struct {
|
type backend struct {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestBackend_basic(t *testing.T) {
|
func TestBackend_basic(t *testing.T) {
|
||||||
var b backend
|
var b *backend
|
||||||
var err error
|
var err error
|
||||||
var storage logical.Storage
|
var storage logical.Storage
|
||||||
factory := func(conf *logical.BackendConfig) (logical.Backend, error) {
|
factory := func(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
@@ -18,7 +18,10 @@ func TestBackend_basic(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
storage = conf.StorageView
|
storage = conf.StorageView
|
||||||
return b.Setup(conf)
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
logicaltest.Test(t, logicaltest.TestCase{
|
logicaltest.Test(t, logicaltest.TestCase{
|
||||||
Factory: factory,
|
Factory: factory,
|
||||||
|
|||||||
@@ -54,7 +54,10 @@ func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return b.Setup(conf)
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend(conf *logical.BackendConfig) (*backend, error) {
|
func Backend(conf *logical.BackendConfig) (*backend, error) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func createBackendWithStorage(t *testing.T) (*backend, logical.Storage) {
|
|||||||
if b == nil {
|
if b == nil {
|
||||||
t.Fatalf("failed to create backend")
|
t.Fatalf("failed to create backend")
|
||||||
}
|
}
|
||||||
_, err = b.Backend.Setup(config)
|
err = b.Backend.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return b.Setup(conf)
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type backend struct {
|
type backend struct {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func TestBackend_CreateParseVerifyRoleTag(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -253,7 +253,7 @@ func TestBackend_ConfigTidyIdentities(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -307,7 +307,7 @@ func TestBackend_ConfigTidyRoleTags(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -361,7 +361,7 @@ func TestBackend_TidyIdentities(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -386,7 +386,7 @@ func TestBackend_TidyRoleTags(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -411,7 +411,7 @@ func TestBackend_ConfigClient(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -548,7 +548,7 @@ func TestBackend_pathConfigCertificate(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -703,7 +703,7 @@ func TestBackend_parseAndVerifyRoleTagValue(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -784,7 +784,7 @@ func TestBackend_PathRoleTag(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -849,7 +849,7 @@ func TestBackend_PathBlacklistRoleTag(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -997,7 +997,7 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndWhitelistIdentity(t *testing.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1177,7 +1177,7 @@ func TestBackend_pathStsConfig(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1325,7 +1325,7 @@ func TestBackendAcc_LoginWithCallerIdentity(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func TestBackend_pathConfigClient(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func TestBackend_pathRoleEc2(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ func Test_enableIamIDResolution(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -221,7 +221,7 @@ func TestBackend_pathIam(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -385,7 +385,7 @@ func TestBackend_pathRoleMixedTypes(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -491,7 +491,7 @@ func TestAwsEc2_RoleCrud(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -617,7 +617,7 @@ func TestAwsEc2_RoleDurationSeconds(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ import (
|
|||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
b := Backend()
|
b := Backend()
|
||||||
_, err := b.Setup(conf)
|
if err := b.Setup(conf); err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return b, err
|
|
||||||
}
|
}
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend().Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend() *backend {
|
func Backend() *backend {
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend().Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend() *backend {
|
func Backend() *backend {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func createBackendWithStorage(t *testing.T) (*backend, logical.Storage) {
|
|||||||
t.Fatalf("failed to create backend")
|
t.Fatalf("failed to create backend")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := b.Backend.Setup(config)
|
err := b.Backend.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend().Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend() *backend {
|
func Backend() *backend {
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend().Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend() *backend {
|
func Backend() *backend {
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend().Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend() *backend {
|
func Backend() *backend {
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend().Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend() *backend {
|
func Backend() *backend {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ func TestBackend_PathListRoles(t *testing.T) {
|
|||||||
config.StorageView = &logical.InmemStorage{}
|
config.StorageView = &logical.InmemStorage{}
|
||||||
|
|
||||||
b := Backend()
|
b := Backend()
|
||||||
if _, err := b.Setup(config); err != nil {
|
if err := b.Setup(config); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,11 @@ import (
|
|||||||
|
|
||||||
// Factory creates a new backend
|
// Factory creates a new backend
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend().Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backend contains the base information for the backend's functionality
|
// Backend contains the base information for the backend's functionality
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend().Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend() *backend {
|
func Backend() *backend {
|
||||||
|
|||||||
@@ -16,7 +16,11 @@ import (
|
|||||||
const databaseConfigPath = "database/config/"
|
const databaseConfigPath = "database/config/"
|
||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend(conf).Setup(conf)
|
b := Backend(conf)
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend(conf *logical.BackendConfig) *databaseBackend {
|
func Backend(conf *logical.BackendConfig) *databaseBackend {
|
||||||
|
|||||||
@@ -12,7 +12,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend().Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend() *framework.Backend {
|
func Backend() *framework.Backend {
|
||||||
|
|||||||
@@ -12,7 +12,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend().Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend() *backend {
|
func Backend() *backend {
|
||||||
|
|||||||
@@ -12,7 +12,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend().Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend() *backend {
|
func Backend() *backend {
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ import (
|
|||||||
|
|
||||||
// Factory creates a new backend implementing the logical.Backend interface
|
// Factory creates a new backend implementing the logical.Backend interface
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend().Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backend returns a new Backend framework struct
|
// Backend returns a new Backend framework struct
|
||||||
|
|||||||
@@ -1870,7 +1870,7 @@ func TestBackend_PathFetchCertList(t *testing.T) {
|
|||||||
config.StorageView = storage
|
config.StorageView = storage
|
||||||
|
|
||||||
b := Backend()
|
b := Backend()
|
||||||
_, err := b.Setup(config)
|
err := b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1997,7 +1997,7 @@ func TestBackend_SignVerbatim(t *testing.T) {
|
|||||||
config.StorageView = storage
|
config.StorageView = storage
|
||||||
|
|
||||||
b := Backend()
|
b := Backend()
|
||||||
_, err := b.Setup(config)
|
err := b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ func createBackendWithStorage(t *testing.T) (*backend, logical.Storage) {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
b := Backend()
|
b := Backend()
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend(conf).Setup(conf)
|
b := Backend(conf)
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend(conf *logical.BackendConfig) *backend {
|
func Backend(conf *logical.BackendConfig) *backend {
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ import (
|
|||||||
|
|
||||||
// Factory creates and configures the backend
|
// Factory creates and configures the backend
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend().Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new backend with all the paths and secrets belonging to it
|
// Creates a new backend with all the paths and secrets belonging to it
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ func TestBackend_config_lease_RU(t *testing.T) {
|
|||||||
config := logical.TestBackendConfig()
|
config := logical.TestBackendConfig()
|
||||||
config.StorageView = &logical.InmemStorage{}
|
config.StorageView = &logical.InmemStorage{}
|
||||||
b := Backend()
|
b := Backend()
|
||||||
if _, err = b.Setup(config); err != nil {
|
if err = b.Setup(config); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return b.Setup(conf)
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend(conf *logical.BackendConfig) (*backend, error) {
|
func Backend(conf *logical.BackendConfig) (*backend, error) {
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ func TestBackend_allowed_users(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func TestSSH_ConfigCAStorageUpgrade(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = b.Setup(config)
|
err = b.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return Backend(conf).Setup(conf)
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend(conf *logical.BackendConfig) *backend {
|
func Backend() *backend {
|
||||||
var b backend
|
var b backend
|
||||||
b.Backend = &framework.Backend{
|
b.Backend = &framework.Backend{
|
||||||
Help: strings.TrimSpace(backendHelp),
|
Help: strings.TrimSpace(backendHelp),
|
||||||
|
|||||||
@@ -10,12 +10,10 @@ import (
|
|||||||
|
|
||||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
b := Backend(conf)
|
b := Backend(conf)
|
||||||
be, err := b.Backend.Setup(conf)
|
if err := b.Setup(conf); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return b, nil
|
||||||
return be, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Backend(conf *logical.BackendConfig) *backend {
|
func Backend(conf *logical.BackendConfig) *backend {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func createBackendWithStorage(t *testing.T) (*backend, logical.Storage) {
|
|||||||
if b == nil {
|
if b == nil {
|
||||||
t.Fatalf("failed to create backend")
|
t.Fatalf("failed to create backend")
|
||||||
}
|
}
|
||||||
_, err := b.Backend.Setup(config)
|
err := b.Backend.Setup(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
39
builtin/plugin/backend.go
Normal file
39
builtin/plugin/backend.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
bplugin "github.com/hashicorp/vault/logical/plugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Factory returns a configured plugin logical.Backend.
|
||||||
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
|
_, ok := conf.Config["plugin_name"]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("plugin_name not provided")
|
||||||
|
}
|
||||||
|
b, err := Backend(conf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backend returns an instance of the backend, either as a plugin if external
|
||||||
|
// or as a concrete implementation if builtin, casted as logical.Backend.
|
||||||
|
func Backend(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
|
name := conf.Config["plugin_name"]
|
||||||
|
sys := conf.System
|
||||||
|
|
||||||
|
b, err := bplugin.NewBackend(name, sys)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
101
builtin/plugin/backend_test.go
Normal file
101
builtin/plugin/backend_test.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/helper/pluginutil"
|
||||||
|
"github.com/hashicorp/vault/http"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
"github.com/hashicorp/vault/logical/plugin"
|
||||||
|
"github.com/hashicorp/vault/logical/plugin/mock"
|
||||||
|
"github.com/hashicorp/vault/vault"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBackend(t *testing.T) {
|
||||||
|
config, cleanup := testConfig(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
_, err := Backend(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackend_Factory(t *testing.T) {
|
||||||
|
config, cleanup := testConfig(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
_, err := Factory(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackend_PluginMain(t *testing.T) {
|
||||||
|
if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content := []byte(vault.TestClusterCACert)
|
||||||
|
tmpfile, err := ioutil.TempFile("", "test-cacert")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer os.Remove(tmpfile.Name()) // clean up
|
||||||
|
|
||||||
|
if _, err := tmpfile.Write(content); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tmpfile.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{"--ca-cert=" + tmpfile.Name()}
|
||||||
|
|
||||||
|
apiClientMeta := &pluginutil.APIClientMeta{}
|
||||||
|
flags := apiClientMeta.FlagSet()
|
||||||
|
flags.Parse(args)
|
||||||
|
tlsConfig := apiClientMeta.GetTLSConfig()
|
||||||
|
tlsProviderFunc := pluginutil.VaultPluginTLSProvider(tlsConfig)
|
||||||
|
|
||||||
|
err = plugin.Serve(&plugin.ServeOpts{
|
||||||
|
BackendFactoryFunc: mock.Factory,
|
||||||
|
TLSProviderFunc: tlsProviderFunc,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testConfig(t *testing.T) (*logical.BackendConfig, func()) {
|
||||||
|
coreConfig := &vault.CoreConfig{}
|
||||||
|
|
||||||
|
cluster := vault.NewTestCluster(t, coreConfig, true)
|
||||||
|
cluster.StartListeners()
|
||||||
|
cores := cluster.Cores
|
||||||
|
|
||||||
|
cores[0].Handler.Handle("/", http.Handler(cores[0].Core))
|
||||||
|
cores[1].Handler.Handle("/", http.Handler(cores[1].Core))
|
||||||
|
cores[2].Handler.Handle("/", http.Handler(cores[2].Core))
|
||||||
|
|
||||||
|
core := cores[0]
|
||||||
|
|
||||||
|
sys := vault.TestDynamicSystemView(core.Core)
|
||||||
|
|
||||||
|
config := &logical.BackendConfig{
|
||||||
|
Logger: nil,
|
||||||
|
System: sys,
|
||||||
|
Config: map[string]string{
|
||||||
|
"plugin_name": "mock-plugin",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMain")
|
||||||
|
|
||||||
|
return config, func() {
|
||||||
|
cluster.CloseListeners()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@ import (
|
|||||||
"github.com/hashicorp/vault/builtin/logical/ssh"
|
"github.com/hashicorp/vault/builtin/logical/ssh"
|
||||||
"github.com/hashicorp/vault/builtin/logical/totp"
|
"github.com/hashicorp/vault/builtin/logical/totp"
|
||||||
"github.com/hashicorp/vault/builtin/logical/transit"
|
"github.com/hashicorp/vault/builtin/logical/transit"
|
||||||
|
"github.com/hashicorp/vault/builtin/plugin"
|
||||||
|
|
||||||
"github.com/hashicorp/vault/audit"
|
"github.com/hashicorp/vault/audit"
|
||||||
"github.com/hashicorp/vault/command"
|
"github.com/hashicorp/vault/command"
|
||||||
@@ -79,6 +80,7 @@ func Commands(metaPtr *meta.Meta) map[string]cli.CommandFactory {
|
|||||||
"ldap": credLdap.Factory,
|
"ldap": credLdap.Factory,
|
||||||
"okta": credOkta.Factory,
|
"okta": credOkta.Factory,
|
||||||
"radius": credRadius.Factory,
|
"radius": credRadius.Factory,
|
||||||
|
"plugin": plugin.Factory,
|
||||||
},
|
},
|
||||||
LogicalBackends: map[string]logical.Factory{
|
LogicalBackends: map[string]logical.Factory{
|
||||||
"aws": aws.Factory,
|
"aws": aws.Factory,
|
||||||
@@ -94,6 +96,7 @@ func Commands(metaPtr *meta.Meta) map[string]cli.CommandFactory {
|
|||||||
"rabbitmq": rabbitmq.Factory,
|
"rabbitmq": rabbitmq.Factory,
|
||||||
"database": database.Factory,
|
"database": database.Factory,
|
||||||
"totp": totp.Factory,
|
"totp": totp.Factory,
|
||||||
|
"plugin": plugin.Factory,
|
||||||
},
|
},
|
||||||
ShutdownCh: command.MakeShutdownCh(),
|
ShutdownCh: command.MakeShutdownCh(),
|
||||||
SighupCh: command.MakeSighupCh(),
|
SighupCh: command.MakeSighupCh(),
|
||||||
|
|||||||
@@ -14,11 +14,12 @@ type AuthEnableCommand struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *AuthEnableCommand) Run(args []string) int {
|
func (c *AuthEnableCommand) Run(args []string) int {
|
||||||
var description, path string
|
var description, path, pluginName string
|
||||||
var local bool
|
var local bool
|
||||||
flags := c.Meta.FlagSet("auth-enable", meta.FlagSetDefault)
|
flags := c.Meta.FlagSet("auth-enable", meta.FlagSetDefault)
|
||||||
flags.StringVar(&description, "description", "", "")
|
flags.StringVar(&description, "description", "", "")
|
||||||
flags.StringVar(&path, "path", "", "")
|
flags.StringVar(&path, "path", "", "")
|
||||||
|
flags.StringVar(&pluginName, "plugin-name", "", "")
|
||||||
flags.BoolVar(&local, "local", false, "")
|
flags.BoolVar(&local, "local", false, "")
|
||||||
flags.Usage = func() { c.Ui.Error(c.Help()) }
|
flags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||||
if err := flags.Parse(args); err != nil {
|
if err := flags.Parse(args); err != nil {
|
||||||
@@ -36,8 +37,13 @@ func (c *AuthEnableCommand) Run(args []string) int {
|
|||||||
authType := args[0]
|
authType := args[0]
|
||||||
|
|
||||||
// If no path is specified, we default the path to the backend type
|
// If no path is specified, we default the path to the backend type
|
||||||
|
// or use the plugin name if it's a plugin backend
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = authType
|
if authType == "plugin" {
|
||||||
|
path = pluginName
|
||||||
|
} else {
|
||||||
|
path = authType
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := c.Client()
|
client, err := c.Client()
|
||||||
@@ -50,6 +56,7 @@ func (c *AuthEnableCommand) Run(args []string) int {
|
|||||||
if err := client.Sys().EnableAuthWithOptions(path, &api.EnableAuthOptions{
|
if err := client.Sys().EnableAuthWithOptions(path, &api.EnableAuthOptions{
|
||||||
Type: authType,
|
Type: authType,
|
||||||
Description: description,
|
Description: description,
|
||||||
|
PluginName: pluginName,
|
||||||
Local: local,
|
Local: local,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf(
|
c.Ui.Error(fmt.Sprintf(
|
||||||
@@ -89,6 +96,9 @@ Auth Enable Options:
|
|||||||
to the type of the mount. This will make the auth
|
to the type of the mount. This will make the auth
|
||||||
provider available at "/auth/<path>"
|
provider available at "/auth/<path>"
|
||||||
|
|
||||||
|
-plugin-name Name of the auth plugin to use based from the name
|
||||||
|
in the plugin catalog.
|
||||||
|
|
||||||
-local Mark the mount as a local mount. Local mounts
|
-local Mark the mount as a local mount. Local mounts
|
||||||
are not replicated nor (if a secondary)
|
are not replicated nor (if a secondary)
|
||||||
removed by replication.
|
removed by replication.
|
||||||
|
|||||||
@@ -14,13 +14,14 @@ type MountCommand struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *MountCommand) Run(args []string) int {
|
func (c *MountCommand) Run(args []string) int {
|
||||||
var description, path, defaultLeaseTTL, maxLeaseTTL string
|
var description, path, defaultLeaseTTL, maxLeaseTTL, pluginName string
|
||||||
var local, forceNoCache bool
|
var local, forceNoCache bool
|
||||||
flags := c.Meta.FlagSet("mount", meta.FlagSetDefault)
|
flags := c.Meta.FlagSet("mount", meta.FlagSetDefault)
|
||||||
flags.StringVar(&description, "description", "", "")
|
flags.StringVar(&description, "description", "", "")
|
||||||
flags.StringVar(&path, "path", "", "")
|
flags.StringVar(&path, "path", "", "")
|
||||||
flags.StringVar(&defaultLeaseTTL, "default-lease-ttl", "", "")
|
flags.StringVar(&defaultLeaseTTL, "default-lease-ttl", "", "")
|
||||||
flags.StringVar(&maxLeaseTTL, "max-lease-ttl", "", "")
|
flags.StringVar(&maxLeaseTTL, "max-lease-ttl", "", "")
|
||||||
|
flags.StringVar(&pluginName, "plugin-name", "", "")
|
||||||
flags.BoolVar(&forceNoCache, "force-no-cache", false, "")
|
flags.BoolVar(&forceNoCache, "force-no-cache", false, "")
|
||||||
flags.BoolVar(&local, "local", false, "")
|
flags.BoolVar(&local, "local", false, "")
|
||||||
flags.Usage = func() { c.Ui.Error(c.Help()) }
|
flags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||||
@@ -39,8 +40,13 @@ func (c *MountCommand) Run(args []string) int {
|
|||||||
mountType := args[0]
|
mountType := args[0]
|
||||||
|
|
||||||
// If no path is specified, we default the path to the backend type
|
// If no path is specified, we default the path to the backend type
|
||||||
|
// or use the plugin name if it's a plugin backend
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = mountType
|
if mountType == "plugin" {
|
||||||
|
path = pluginName
|
||||||
|
} else {
|
||||||
|
path = mountType
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := c.Client()
|
client, err := c.Client()
|
||||||
@@ -57,6 +63,7 @@ func (c *MountCommand) Run(args []string) int {
|
|||||||
DefaultLeaseTTL: defaultLeaseTTL,
|
DefaultLeaseTTL: defaultLeaseTTL,
|
||||||
MaxLeaseTTL: maxLeaseTTL,
|
MaxLeaseTTL: maxLeaseTTL,
|
||||||
ForceNoCache: forceNoCache,
|
ForceNoCache: forceNoCache,
|
||||||
|
PluginName: pluginName,
|
||||||
},
|
},
|
||||||
Local: local,
|
Local: local,
|
||||||
}
|
}
|
||||||
@@ -67,9 +74,14 @@ func (c *MountCommand) Run(args []string) int {
|
|||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mountPart := fmt.Sprintf("'%s'", mountType)
|
||||||
|
if mountType == "plugin" {
|
||||||
|
mountPart = fmt.Sprintf("plugin '%s'", pluginName)
|
||||||
|
}
|
||||||
|
|
||||||
c.Ui.Output(fmt.Sprintf(
|
c.Ui.Output(fmt.Sprintf(
|
||||||
"Successfully mounted '%s' at '%s'!",
|
"Successfully mounted %s at '%s'!",
|
||||||
mountType, path))
|
mountPart, path))
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -112,10 +124,12 @@ Mount Options:
|
|||||||
not affect caching of the underlying encrypted
|
not affect caching of the underlying encrypted
|
||||||
data storage.
|
data storage.
|
||||||
|
|
||||||
|
-plugin-name Name of the plugin to mount based from the name
|
||||||
|
in the plugin catalog.
|
||||||
|
|
||||||
-local Mark the mount as a local mount. Local mounts
|
-local Mark the mount as a local mount. Local mounts
|
||||||
are not replicated nor (if a secondary)
|
are not replicated nor (if a secondary)
|
||||||
removed by replication.
|
removed by replication.
|
||||||
|
|
||||||
`
|
`
|
||||||
return strings.TrimSpace(helpText)
|
return strings.TrimSpace(helpText)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,9 +42,13 @@ func (c *MountsCommand) Run(args []string) int {
|
|||||||
}
|
}
|
||||||
sort.Strings(paths)
|
sort.Strings(paths)
|
||||||
|
|
||||||
columns := []string{"Path | Type | Accessor | Default TTL | Max TTL | Force No Cache | Replication Behavior | Description"}
|
columns := []string{"Path | Type | Accessor | Plugin | Default TTL | Max TTL | Force No Cache | Replication Behavior | Description"}
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
mount := mounts[path]
|
mount := mounts[path]
|
||||||
|
pluginName := "n/a"
|
||||||
|
if mount.Config.PluginName != "" {
|
||||||
|
pluginName = mount.Config.PluginName
|
||||||
|
}
|
||||||
defTTL := "system"
|
defTTL := "system"
|
||||||
switch {
|
switch {
|
||||||
case mount.Type == "system":
|
case mount.Type == "system":
|
||||||
@@ -68,7 +72,7 @@ func (c *MountsCommand) Run(args []string) int {
|
|||||||
replicatedBehavior = "local"
|
replicatedBehavior = "local"
|
||||||
}
|
}
|
||||||
columns = append(columns, fmt.Sprintf(
|
columns = append(columns, fmt.Sprintf(
|
||||||
"%s | %s | %s | %s | %s | %v | %s | %s", path, mount.Type, mount.Accessor, defTTL, maxTTL,
|
"%s | %s | %s | %s | %s | %s | %v | %s | %s", path, mount.Type, mount.Accessor, pluginName, defTTL, maxTTL,
|
||||||
mount.Config.ForceNoCache, replicatedBehavior, mount.Description))
|
mount.Config.ForceNoCache, replicatedBehavior, mount.Description))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -199,8 +199,8 @@ func TestRekey_init_pgp(t *testing.T) {
|
|||||||
MaxLeaseTTLVal: time.Hour * 24 * 32,
|
MaxLeaseTTLVal: time.Hour * 24 * 32,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
sysBE := vault.NewSystemBackend(core)
|
sysBackend := vault.NewSystemBackend(core)
|
||||||
sysBackend, err := sysBE.Backend.Setup(bc)
|
err := sysBackend.Backend.Setup(bc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ import (
|
|||||||
"github.com/hashicorp/vault/plugins/database/postgresql"
|
"github.com/hashicorp/vault/plugins/database/postgresql"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BuiltinFactory is the func signature that should be returned by
|
||||||
|
// the plugin's New() func.
|
||||||
type BuiltinFactory func() (interface{}, error)
|
type BuiltinFactory func() (interface{}, error)
|
||||||
|
|
||||||
var plugins map[string]BuiltinFactory = map[string]BuiltinFactory{
|
var plugins = map[string]BuiltinFactory{
|
||||||
// These four plugins all use the same mysql implementation but with
|
// These four plugins all use the same mysql implementation but with
|
||||||
// different username settings passed by the constructor.
|
// different username settings passed by the constructor.
|
||||||
"mysql-database-plugin": mysql.New(mysql.MetadataLen, mysql.UsernameLen),
|
"mysql-database-plugin": mysql.New(mysql.MetadataLen, mysql.UsernameLen),
|
||||||
@@ -26,11 +28,14 @@ var plugins map[string]BuiltinFactory = map[string]BuiltinFactory{
|
|||||||
"hana-database-plugin": hana.New,
|
"hana-database-plugin": hana.New,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get returns the BuiltinFactory func for a particular backend plugin
|
||||||
|
// from the plugins map.
|
||||||
func Get(name string) (BuiltinFactory, bool) {
|
func Get(name string) (BuiltinFactory, bool) {
|
||||||
f, ok := plugins[name]
|
f, ok := plugins[name]
|
||||||
return f, ok
|
return f, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keys returns the list of plugin names that are considered builtin plugins.
|
||||||
func Keys() []string {
|
func Keys() []string {
|
||||||
keys := make([]string, len(plugins))
|
keys := make([]string, len(plugins))
|
||||||
|
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ type LookRunnerUtil interface {
|
|||||||
// PluginRunner defines the metadata needed to run a plugin securely with
|
// PluginRunner defines the metadata needed to run a plugin securely with
|
||||||
// go-plugin.
|
// go-plugin.
|
||||||
type PluginRunner struct {
|
type PluginRunner struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name" structs:"name"`
|
||||||
Command string `json:"command"`
|
Command string `json:"command" structs:"command"`
|
||||||
Args []string `json:"args"`
|
Args []string `json:"args" structs:"args"`
|
||||||
Sha256 []byte `json:"sha256"`
|
Sha256 []byte `json:"sha256" structs:"sha256"`
|
||||||
Builtin bool `json:"builtin"`
|
Builtin bool `json:"builtin" structs:"builtin"`
|
||||||
BuiltinFactory func() (interface{}, error) `json:"-"`
|
BuiltinFactory func() (interface{}, error) `json:"-" structs:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run takes a wrapper instance, and the go-plugin paramaters and executes a
|
// Run takes a wrapper instance, and the go-plugin paramaters and executes a
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ func VaultPluginTLSProvider(apiTLSConfig *api.TLSConfig) func() (*tls.Config, er
|
|||||||
// Parse the JWT and retrieve the vault address
|
// Parse the JWT and retrieve the vault address
|
||||||
wt, err := jws.ParseJWT([]byte(unwrapToken))
|
wt, err := jws.ParseJWT([]byte(unwrapToken))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New(fmt.Sprintf("error decoding token: %s", err))
|
return nil, fmt.Errorf("error decoding token: %s", err)
|
||||||
}
|
}
|
||||||
if wt == nil {
|
if wt == nil {
|
||||||
return nil, errors.New("nil decoded token")
|
return nil, errors.New("nil decoded token")
|
||||||
@@ -146,7 +146,7 @@ func VaultPluginTLSProvider(apiTLSConfig *api.TLSConfig) func() (*tls.Config, er
|
|||||||
|
|
||||||
// Sanity check the value
|
// Sanity check the value
|
||||||
if _, err := url.Parse(vaultAddr); err != nil {
|
if _, err := url.Parse(vaultAddr); err != nil {
|
||||||
return nil, errors.New(fmt.Sprintf("error parsing the vault address: %s", err))
|
return nil, fmt.Errorf("error parsing the vault address: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unwrap the token
|
// Unwrap the token
|
||||||
@@ -165,7 +165,7 @@ func VaultPluginTLSProvider(apiTLSConfig *api.TLSConfig) func() (*tls.Config, er
|
|||||||
return nil, errwrap.Wrapf("error during token unwrap request: {{err}}", err)
|
return nil, errwrap.Wrapf("error during token unwrap request: {{err}}", err)
|
||||||
}
|
}
|
||||||
if secret == nil {
|
if secret == nil {
|
||||||
return nil, errors.New("error during token unwrap request secret is nil")
|
return nil, errors.New("error during token unwrap request: secret is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve and parse the server's certificate
|
// Retrieve and parse the server's certificate
|
||||||
|
|||||||
@@ -82,6 +82,12 @@ type Backend struct {
|
|||||||
// See the built-in AuthRenew helpers in lease.go for common callbacks.
|
// See the built-in AuthRenew helpers in lease.go for common callbacks.
|
||||||
AuthRenew OperationFunc
|
AuthRenew OperationFunc
|
||||||
|
|
||||||
|
// LicenseRegistration is called to register the license for a backend.
|
||||||
|
LicenseRegistration LicenseRegistrationFunc
|
||||||
|
|
||||||
|
// Type is the logical.BackendType for the backend implementation
|
||||||
|
BackendType logical.BackendType
|
||||||
|
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
system logical.SystemView
|
system logical.SystemView
|
||||||
once sync.Once
|
once sync.Once
|
||||||
@@ -107,6 +113,10 @@ type InitializeFunc func() error
|
|||||||
// InvalidateFunc is the callback for backend key invalidation.
|
// InvalidateFunc is the callback for backend key invalidation.
|
||||||
type InvalidateFunc func(string)
|
type InvalidateFunc func(string)
|
||||||
|
|
||||||
|
// LicenseRegistrationFunc is the callback for backend license registration.
|
||||||
|
type LicenseRegistrationFunc func(interface{}) error
|
||||||
|
|
||||||
|
// HandleExistenceCheck is the logical.Backend implementation.
|
||||||
func (b *Backend) HandleExistenceCheck(req *logical.Request) (checkFound bool, exists bool, err error) {
|
func (b *Backend) HandleExistenceCheck(req *logical.Request) (checkFound bool, exists bool, err error) {
|
||||||
b.once.Do(b.init)
|
b.once.Do(b.init)
|
||||||
|
|
||||||
@@ -154,7 +164,7 @@ func (b *Backend) HandleExistenceCheck(req *logical.Request) (checkFound bool, e
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// logical.Backend impl.
|
// HandleRequest is the logical.Backend implementation.
|
||||||
func (b *Backend) HandleRequest(req *logical.Request) (*logical.Response, error) {
|
func (b *Backend) HandleRequest(req *logical.Request) (*logical.Response, error) {
|
||||||
b.once.Do(b.init)
|
b.once.Do(b.init)
|
||||||
|
|
||||||
@@ -221,18 +231,11 @@ func (b *Backend) HandleRequest(req *logical.Request) (*logical.Response, error)
|
|||||||
return callback(req, &fd)
|
return callback(req, &fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// logical.Backend impl.
|
// SpecialPaths is the logical.Backend implementation.
|
||||||
func (b *Backend) SpecialPaths() *logical.Paths {
|
func (b *Backend) SpecialPaths() *logical.Paths {
|
||||||
return b.PathsSpecial
|
return b.PathsSpecial
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup is used to initialize the backend with the initial backend configuration
|
|
||||||
func (b *Backend) Setup(config *logical.BackendConfig) (logical.Backend, error) {
|
|
||||||
b.logger = config.Logger
|
|
||||||
b.system = config.System
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup is used to release resources and prepare to stop the backend
|
// Cleanup is used to release resources and prepare to stop the backend
|
||||||
func (b *Backend) Cleanup() {
|
func (b *Backend) Cleanup() {
|
||||||
if b.Clean != nil {
|
if b.Clean != nil {
|
||||||
@@ -240,6 +243,7 @@ func (b *Backend) Cleanup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize calls the backend's Init func if set.
|
||||||
func (b *Backend) Initialize() error {
|
func (b *Backend) Initialize() error {
|
||||||
if b.Init != nil {
|
if b.Init != nil {
|
||||||
return b.Init()
|
return b.Init()
|
||||||
@@ -255,6 +259,13 @@ func (b *Backend) InvalidateKey(key string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup is used to initialize the backend with the initial backend configuration
|
||||||
|
func (b *Backend) Setup(config *logical.BackendConfig) error {
|
||||||
|
b.logger = config.Logger
|
||||||
|
b.system = config.System
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Logger can be used to get the logger. If no logger has been set,
|
// Logger can be used to get the logger. If no logger has been set,
|
||||||
// the logs will be discarded.
|
// the logs will be discarded.
|
||||||
func (b *Backend) Logger() log.Logger {
|
func (b *Backend) Logger() log.Logger {
|
||||||
@@ -265,11 +276,25 @@ func (b *Backend) Logger() log.Logger {
|
|||||||
return logformat.NewVaultLoggerWithWriter(ioutil.Discard, log.LevelOff)
|
return logformat.NewVaultLoggerWithWriter(ioutil.Discard, log.LevelOff)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// System returns the backend's system view.
|
||||||
func (b *Backend) System() logical.SystemView {
|
func (b *Backend) System() logical.SystemView {
|
||||||
return b.system
|
return b.system
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method takes in the TTL and MaxTTL values provided by the user,
|
// Type returns the backend type
|
||||||
|
func (b *Backend) Type() logical.BackendType {
|
||||||
|
return b.BackendType
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterLicense performs backend license registration.
|
||||||
|
func (b *Backend) RegisterLicense(license interface{}) error {
|
||||||
|
if b.LicenseRegistration == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return b.LicenseRegistration(license)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SanitizeTTLStr takes in the TTL and MaxTTL values provided by the user,
|
||||||
// compares those with the SystemView values. If they are empty a value of 0 is
|
// compares those with the SystemView values. If they are empty a value of 0 is
|
||||||
// set, which will cause initial secret or LeaseExtend operations to use the
|
// set, which will cause initial secret or LeaseExtend operations to use the
|
||||||
// mount/system defaults. If they are set, their boundaries are validated.
|
// mount/system defaults. If they are set, their boundaries are validated.
|
||||||
@@ -297,7 +322,8 @@ func (b *Backend) SanitizeTTLStr(ttlStr, maxTTLStr string) (ttl, maxTTL time.Dur
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Caps the boundaries of ttl and max_ttl values to the backend mount's max_ttl value.
|
// SanitizeTTL caps the boundaries of ttl and max_ttl values to the
|
||||||
|
// backend mount's max_ttl value.
|
||||||
func (b *Backend) SanitizeTTL(ttl, maxTTL time.Duration) (time.Duration, time.Duration, error) {
|
func (b *Backend) SanitizeTTL(ttl, maxTTL time.Duration) (time.Duration, time.Duration, error) {
|
||||||
sysMaxTTL := b.System().MaxLeaseTTL()
|
sysMaxTTL := b.System().MaxLeaseTTL()
|
||||||
if ttl > sysMaxTTL {
|
if ttl > sysMaxTTL {
|
||||||
@@ -575,6 +601,7 @@ func (s *FieldSchema) DefaultOrZero() interface{} {
|
|||||||
return s.Type.Zero()
|
return s.Type.Zero()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Zero returns the correct zero-value for a specific FieldType
|
||||||
func (t FieldType) Zero() interface{} {
|
func (t FieldType) Zero() interface{} {
|
||||||
switch t {
|
switch t {
|
||||||
case TypeString:
|
case TypeString:
|
||||||
|
|||||||
@@ -2,6 +2,29 @@ package logical
|
|||||||
|
|
||||||
import log "github.com/mgutz/logxi/v1"
|
import log "github.com/mgutz/logxi/v1"
|
||||||
|
|
||||||
|
// BackendType is the type of backend that is being implemented
|
||||||
|
type BackendType uint32
|
||||||
|
|
||||||
|
// The these are the types of backends that can be derived from
|
||||||
|
// logical.Backend
|
||||||
|
const (
|
||||||
|
TypeUnknown BackendType = 0 // This is also the zero-value for BackendType
|
||||||
|
TypeLogical BackendType = 1
|
||||||
|
TypeCredential BackendType = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stringer implementation
|
||||||
|
func (b BackendType) String() string {
|
||||||
|
switch b {
|
||||||
|
case TypeLogical:
|
||||||
|
return "secret"
|
||||||
|
case TypeCredential:
|
||||||
|
return "auth"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
// Backend interface must be implemented to be "mountable" at
|
// Backend interface must be implemented to be "mountable" at
|
||||||
// a given path. Requests flow through a router which has various mount
|
// a given path. Requests flow through a router which has various mount
|
||||||
// points that flow to a logical backend. The logic of each backend is flexible,
|
// points that flow to a logical backend. The logic of each backend is flexible,
|
||||||
@@ -27,6 +50,11 @@ type Backend interface {
|
|||||||
// information, such as globally configured default and max lease TTLs.
|
// information, such as globally configured default and max lease TTLs.
|
||||||
System() SystemView
|
System() SystemView
|
||||||
|
|
||||||
|
// Logger provides an interface to access the underlying logger. This
|
||||||
|
// is useful when a struct embeds a Backend-implemented struct that
|
||||||
|
// contains a private instance of logger.
|
||||||
|
Logger() log.Logger
|
||||||
|
|
||||||
// HandleExistenceCheck is used to handle a request and generate a response
|
// HandleExistenceCheck is used to handle a request and generate a response
|
||||||
// indicating whether the given path exists or not; this is used to
|
// indicating whether the given path exists or not; this is used to
|
||||||
// understand whether the request must have a Create or Update capability
|
// understand whether the request must have a Create or Update capability
|
||||||
@@ -47,6 +75,16 @@ type Backend interface {
|
|||||||
// to the backend. The backend can use this to clear any caches or reset
|
// to the backend. The backend can use this to clear any caches or reset
|
||||||
// internal state as needed.
|
// internal state as needed.
|
||||||
InvalidateKey(key string)
|
InvalidateKey(key string)
|
||||||
|
|
||||||
|
// Setup is used to set up the backend based on the provided backend
|
||||||
|
// configuration.
|
||||||
|
Setup(*BackendConfig) error
|
||||||
|
|
||||||
|
// Type returns the BackendType for the particular backend
|
||||||
|
Type() BackendType
|
||||||
|
|
||||||
|
// RegisterLicense performs backend license registration
|
||||||
|
RegisterLicense(interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// BackendConfig is provided to the factory to initialize the backend
|
// BackendConfig is provided to the factory to initialize the backend
|
||||||
|
|||||||
23
logical/plugin/backend.go
Normal file
23
logical/plugin/backend.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/rpc"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackendPlugin is the plugin.Plugin implementation
|
||||||
|
type BackendPlugin struct {
|
||||||
|
Factory func(*logical.BackendConfig) (logical.Backend, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server gets called when on plugin.Serve()
|
||||||
|
func (b *BackendPlugin) Server(broker *plugin.MuxBroker) (interface{}, error) {
|
||||||
|
return &backendPluginServer{factory: b.Factory, broker: broker}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client gets called on plugin.NewClient()
|
||||||
|
func (b BackendPlugin) Client(broker *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
|
||||||
|
return &backendPluginClient{client: c, broker: broker}, nil
|
||||||
|
}
|
||||||
228
logical/plugin/backend_client.go
Normal file
228
logical/plugin/backend_client.go
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/rpc"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
log "github.com/mgutz/logxi/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// backendPluginClient implements logical.Backend and is the
|
||||||
|
// go-plugin client.
|
||||||
|
type backendPluginClient struct {
|
||||||
|
broker *plugin.MuxBroker
|
||||||
|
client *rpc.Client
|
||||||
|
pluginClient *plugin.Client
|
||||||
|
|
||||||
|
system logical.SystemView
|
||||||
|
logger log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleRequestArgs is the args for HandleRequest method.
|
||||||
|
type HandleRequestArgs struct {
|
||||||
|
StorageID uint32
|
||||||
|
Request *logical.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleRequestReply is the reply for HandleRequest method.
|
||||||
|
type HandleRequestReply struct {
|
||||||
|
Response *logical.Response
|
||||||
|
Error *plugin.BasicError
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpecialPathsReply is the reply for SpecialPaths method.
|
||||||
|
type SpecialPathsReply struct {
|
||||||
|
Paths *logical.Paths
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemReply is the reply for System method.
|
||||||
|
type SystemReply struct {
|
||||||
|
SystemView logical.SystemView
|
||||||
|
Error *plugin.BasicError
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleExistenceCheckArgs is the args for HandleExistenceCheck method.
|
||||||
|
type HandleExistenceCheckArgs struct {
|
||||||
|
StorageID uint32
|
||||||
|
Request *logical.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleExistenceCheckReply is the reply for HandleExistenceCheck method.
|
||||||
|
type HandleExistenceCheckReply struct {
|
||||||
|
CheckFound bool
|
||||||
|
Exists bool
|
||||||
|
Error *plugin.BasicError
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupArgs is the args for Setup method.
|
||||||
|
type SetupArgs struct {
|
||||||
|
StorageID uint32
|
||||||
|
LoggerID uint32
|
||||||
|
SysViewID uint32
|
||||||
|
Config map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupReply is the reply for Setup method.
|
||||||
|
type SetupReply struct {
|
||||||
|
Error *plugin.BasicError
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeReply is the reply for the Type method.
|
||||||
|
type TypeReply struct {
|
||||||
|
Type logical.BackendType
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterLicenseArgs is the args for the RegisterLicense method.
|
||||||
|
type RegisterLicenseArgs struct {
|
||||||
|
License interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterLicenseReply is the reply for the RegisterLicense method.
|
||||||
|
type RegisterLicenseReply struct {
|
||||||
|
Error *plugin.BasicError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginClient) HandleRequest(req *logical.Request) (*logical.Response, error) {
|
||||||
|
args := &HandleRequestArgs{
|
||||||
|
Request: req,
|
||||||
|
}
|
||||||
|
var reply HandleRequestReply
|
||||||
|
|
||||||
|
err := b.client.Call("Plugin.HandleRequest", args, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if reply.Error != nil {
|
||||||
|
if reply.Error.Error() == logical.ErrUnsupportedOperation.Error() {
|
||||||
|
return nil, logical.ErrUnsupportedOperation
|
||||||
|
}
|
||||||
|
return nil, reply.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.Response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginClient) SpecialPaths() *logical.Paths {
|
||||||
|
var reply SpecialPathsReply
|
||||||
|
err := b.client.Call("Plugin.SpecialPaths", new(interface{}), &reply)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.Paths
|
||||||
|
}
|
||||||
|
|
||||||
|
// System returns vault's system view. The backend client stores the view during
|
||||||
|
// Setup, so there is no need to shim the system just to get it back.
|
||||||
|
func (b *backendPluginClient) System() logical.SystemView {
|
||||||
|
return b.system
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logger returns vault's logger. The backend client stores the logger during
|
||||||
|
// Setup, so there is no need to shim the logger just to get it back.
|
||||||
|
func (b *backendPluginClient) Logger() log.Logger {
|
||||||
|
return b.logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginClient) HandleExistenceCheck(req *logical.Request) (bool, bool, error) {
|
||||||
|
args := &HandleExistenceCheckArgs{
|
||||||
|
Request: req,
|
||||||
|
}
|
||||||
|
var reply HandleExistenceCheckReply
|
||||||
|
|
||||||
|
err := b.client.Call("Plugin.HandleExistenceCheck", args, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return false, false, err
|
||||||
|
}
|
||||||
|
if reply.Error != nil {
|
||||||
|
// THINKING: Should be be a switch on all error types?
|
||||||
|
if reply.Error.Error() == logical.ErrUnsupportedPath.Error() {
|
||||||
|
return false, false, logical.ErrUnsupportedPath
|
||||||
|
}
|
||||||
|
return false, false, reply.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.CheckFound, reply.Exists, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginClient) Cleanup() {
|
||||||
|
b.client.Call("Plugin.Cleanup", new(interface{}), &struct{}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginClient) Initialize() error {
|
||||||
|
err := b.client.Call("Plugin.Initialize", new(interface{}), &struct{}{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginClient) InvalidateKey(key string) {
|
||||||
|
b.client.Call("Plugin.InvalidateKey", key, &struct{}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginClient) Setup(config *logical.BackendConfig) error {
|
||||||
|
// Shim logical.Storage
|
||||||
|
storageID := b.broker.NextId()
|
||||||
|
go b.broker.AcceptAndServe(storageID, &StorageServer{
|
||||||
|
impl: config.StorageView,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Shim log.Logger
|
||||||
|
loggerID := b.broker.NextId()
|
||||||
|
go b.broker.AcceptAndServe(loggerID, &LoggerServer{
|
||||||
|
logger: config.Logger,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Shim logical.SystemView
|
||||||
|
sysViewID := b.broker.NextId()
|
||||||
|
go b.broker.AcceptAndServe(sysViewID, &SystemViewServer{
|
||||||
|
impl: config.System,
|
||||||
|
})
|
||||||
|
|
||||||
|
args := &SetupArgs{
|
||||||
|
StorageID: storageID,
|
||||||
|
LoggerID: loggerID,
|
||||||
|
SysViewID: sysViewID,
|
||||||
|
Config: config.Config,
|
||||||
|
}
|
||||||
|
var reply SetupReply
|
||||||
|
|
||||||
|
err := b.client.Call("Plugin.Setup", args, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if reply.Error != nil {
|
||||||
|
return reply.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set system and logger for getter methods
|
||||||
|
b.system = config.System
|
||||||
|
b.logger = config.Logger
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginClient) Type() logical.BackendType {
|
||||||
|
var reply TypeReply
|
||||||
|
err := b.client.Call("Plugin.Type", new(interface{}), &reply)
|
||||||
|
if err != nil {
|
||||||
|
return logical.TypeUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
return logical.BackendType(reply.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginClient) RegisterLicense(license interface{}) error {
|
||||||
|
var reply RegisterLicenseReply
|
||||||
|
args := RegisterLicenseArgs{
|
||||||
|
License: license,
|
||||||
|
}
|
||||||
|
err := b.client.Call("Plugin.RegisterLicense", args, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if reply.Error != nil {
|
||||||
|
return reply.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
156
logical/plugin/backend_server.go
Normal file
156
logical/plugin/backend_server.go
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/rpc"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
)
|
||||||
|
|
||||||
|
// backendPluginServer is the RPC server that backendPluginClient talks to,
|
||||||
|
// it methods conforming to requirements by net/rpc
|
||||||
|
type backendPluginServer struct {
|
||||||
|
broker *plugin.MuxBroker
|
||||||
|
backend logical.Backend
|
||||||
|
factory func(*logical.BackendConfig) (logical.Backend, error)
|
||||||
|
|
||||||
|
loggerClient *rpc.Client
|
||||||
|
sysViewClient *rpc.Client
|
||||||
|
storageClient *rpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginServer) HandleRequest(args *HandleRequestArgs, reply *HandleRequestReply) error {
|
||||||
|
storage := &StorageClient{client: b.storageClient}
|
||||||
|
args.Request.Storage = storage
|
||||||
|
|
||||||
|
resp, err := b.backend.HandleRequest(args.Request)
|
||||||
|
*reply = HandleRequestReply{
|
||||||
|
Response: resp,
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginServer) SpecialPaths(_ interface{}, reply *SpecialPathsReply) error {
|
||||||
|
*reply = SpecialPathsReply{
|
||||||
|
Paths: b.backend.SpecialPaths(),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginServer) HandleExistenceCheck(args *HandleExistenceCheckArgs, reply *HandleExistenceCheckReply) error {
|
||||||
|
storage := &StorageClient{client: b.storageClient}
|
||||||
|
args.Request.Storage = storage
|
||||||
|
|
||||||
|
checkFound, exists, err := b.backend.HandleExistenceCheck(args.Request)
|
||||||
|
*reply = HandleExistenceCheckReply{
|
||||||
|
CheckFound: checkFound,
|
||||||
|
Exists: exists,
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginServer) Cleanup(_ interface{}, _ *struct{}) error {
|
||||||
|
b.backend.Cleanup()
|
||||||
|
|
||||||
|
// Close rpc clients
|
||||||
|
b.loggerClient.Close()
|
||||||
|
b.sysViewClient.Close()
|
||||||
|
b.storageClient.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginServer) Initialize(_ interface{}, _ *struct{}) error {
|
||||||
|
err := b.backend.Initialize()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginServer) InvalidateKey(args string, _ *struct{}) error {
|
||||||
|
b.backend.InvalidateKey(args)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup dials into the plugin's broker to get a shimmed storage, logger, and
|
||||||
|
// system view of the backend. This method also instantiates the underlying
|
||||||
|
// backend through its factory func for the server side of the plugin.
|
||||||
|
func (b *backendPluginServer) Setup(args *SetupArgs, reply *SetupReply) error {
|
||||||
|
// Dial for storage
|
||||||
|
storageConn, err := b.broker.Dial(args.StorageID)
|
||||||
|
if err != nil {
|
||||||
|
*reply = SetupReply{
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rawStorageClient := rpc.NewClient(storageConn)
|
||||||
|
b.storageClient = rawStorageClient
|
||||||
|
|
||||||
|
storage := &StorageClient{client: rawStorageClient}
|
||||||
|
|
||||||
|
// Dial for logger
|
||||||
|
loggerConn, err := b.broker.Dial(args.LoggerID)
|
||||||
|
if err != nil {
|
||||||
|
*reply = SetupReply{
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rawLoggerClient := rpc.NewClient(loggerConn)
|
||||||
|
b.loggerClient = rawLoggerClient
|
||||||
|
|
||||||
|
logger := &LoggerClient{client: rawLoggerClient}
|
||||||
|
|
||||||
|
// Dial for sys view
|
||||||
|
sysViewConn, err := b.broker.Dial(args.SysViewID)
|
||||||
|
if err != nil {
|
||||||
|
*reply = SetupReply{
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rawSysViewClient := rpc.NewClient(sysViewConn)
|
||||||
|
b.sysViewClient = rawSysViewClient
|
||||||
|
|
||||||
|
sysView := &SystemViewClient{client: rawSysViewClient}
|
||||||
|
|
||||||
|
config := &logical.BackendConfig{
|
||||||
|
StorageView: storage,
|
||||||
|
Logger: logger,
|
||||||
|
System: sysView,
|
||||||
|
Config: args.Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the underlying backend factory after shims have been created
|
||||||
|
// to set b.backend
|
||||||
|
backend, err := b.factory(config)
|
||||||
|
if err != nil {
|
||||||
|
*reply = SetupReply{
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.backend = backend
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginServer) Type(_ interface{}, reply *TypeReply) error {
|
||||||
|
*reply = TypeReply{
|
||||||
|
Type: b.backend.Type(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendPluginServer) RegisterLicense(args *RegisterLicenseArgs, reply *RegisterLicenseReply) error {
|
||||||
|
err := b.backend.RegisterLicense(args.License)
|
||||||
|
if err != nil {
|
||||||
|
*reply = RegisterLicenseReply{
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
176
logical/plugin/backend_test.go
Normal file
176
logical/plugin/backend_test.go
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
gplugin "github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/vault/helper/logformat"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
"github.com/hashicorp/vault/logical/plugin/mock"
|
||||||
|
log "github.com/mgutz/logxi/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBackendPlugin_impl(t *testing.T) {
|
||||||
|
var _ gplugin.Plugin = new(BackendPlugin)
|
||||||
|
var _ logical.Backend = new(backendPluginClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackendPlugin_HandleRequest(t *testing.T) {
|
||||||
|
b, cleanup := testBackend(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
resp, err := b.HandleRequest(&logical.Request{
|
||||||
|
Operation: logical.ReadOperation,
|
||||||
|
Path: "test/ing",
|
||||||
|
Data: map[string]interface{}{"value": "foo"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp.Data["value"] != "foo" {
|
||||||
|
t.Fatalf("bad: %#v", resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackendPlugin_SpecialPaths(t *testing.T) {
|
||||||
|
b, cleanup := testBackend(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
paths := b.SpecialPaths()
|
||||||
|
if paths == nil {
|
||||||
|
t.Fatal("SpecialPaths() returned nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackendPlugin_System(t *testing.T) {
|
||||||
|
b, cleanup := testBackend(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
sys := b.System()
|
||||||
|
if sys == nil {
|
||||||
|
t.Fatal("System() returned nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := sys.DefaultLeaseTTL()
|
||||||
|
expected := 300 * time.Second
|
||||||
|
|
||||||
|
if actual != expected {
|
||||||
|
t.Fatalf("bad: %v, expected %v", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackendPlugin_Logger(t *testing.T) {
|
||||||
|
b, cleanup := testBackend(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
logger := b.Logger()
|
||||||
|
if logger == nil {
|
||||||
|
t.Fatal("Logger() returned nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackendPlugin_HandleExistenceCheck(t *testing.T) {
|
||||||
|
b, cleanup := testBackend(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
checkFound, exists, err := b.HandleExistenceCheck(&logical.Request{
|
||||||
|
Operation: logical.CreateOperation,
|
||||||
|
Path: "test/ing",
|
||||||
|
Data: map[string]interface{}{"value": "foo"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !checkFound {
|
||||||
|
t.Fatal("existence check not found for path 'test/ing'")
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
t.Fatal("existence check should have returned 'false' for 'testing/read'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackendPlugin_Cleanup(t *testing.T) {
|
||||||
|
b, cleanup := testBackend(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
b.Cleanup()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackendPlugin_Initialize(t *testing.T) {
|
||||||
|
b, cleanup := testBackend(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
err := b.Initialize()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackendPlugin_InvalidateKey(t *testing.T) {
|
||||||
|
b, cleanup := testBackend(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
resp, err := b.HandleRequest(&logical.Request{
|
||||||
|
Operation: logical.ReadOperation,
|
||||||
|
Path: "internal",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp.Data["value"] == "" {
|
||||||
|
t.Fatalf("bad: %#v, expected non-empty value", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.InvalidateKey("internal")
|
||||||
|
|
||||||
|
resp, err = b.HandleRequest(&logical.Request{
|
||||||
|
Operation: logical.ReadOperation,
|
||||||
|
Path: "internal",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp.Data["value"] != "" {
|
||||||
|
t.Fatalf("bad: expected empty response data, got %#v", resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackendPlugin_Setup(t *testing.T) {
|
||||||
|
_, cleanup := testBackend(t)
|
||||||
|
defer cleanup()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBackend(t *testing.T) (logical.Backend, func()) {
|
||||||
|
// Create a mock provider
|
||||||
|
pluginMap := map[string]gplugin.Plugin{
|
||||||
|
"backend": &BackendPlugin{
|
||||||
|
Factory: mock.Factory,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
client, _ := gplugin.TestPluginRPCConn(t, pluginMap)
|
||||||
|
cleanup := func() {
|
||||||
|
client.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request the backend
|
||||||
|
raw, err := client.Dispense(BackendPluginName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
b := raw.(logical.Backend)
|
||||||
|
|
||||||
|
err = b.Setup(&logical.BackendConfig{
|
||||||
|
Logger: logformat.NewVaultLogger(log.LevelTrace),
|
||||||
|
System: &logical.StaticSystemView{
|
||||||
|
DefaultLeaseTTLVal: 300 * time.Second,
|
||||||
|
MaxLeaseTTLVal: 1800 * time.Second,
|
||||||
|
},
|
||||||
|
StorageView: &logical.InmemStorage{},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b, cleanup
|
||||||
|
}
|
||||||
205
logical/plugin/logger.go
Normal file
205
logical/plugin/logger.go
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/rpc"
|
||||||
|
|
||||||
|
plugin "github.com/hashicorp/go-plugin"
|
||||||
|
log "github.com/mgutz/logxi/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoggerClient struct {
|
||||||
|
client *rpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerClient) Trace(msg string, args ...interface{}) {
|
||||||
|
cArgs := &LoggerArgs{
|
||||||
|
Msg: msg,
|
||||||
|
Args: args,
|
||||||
|
}
|
||||||
|
l.client.Call("Plugin.Trace", cArgs, &struct{}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerClient) Debug(msg string, args ...interface{}) {
|
||||||
|
cArgs := &LoggerArgs{
|
||||||
|
Msg: msg,
|
||||||
|
Args: args,
|
||||||
|
}
|
||||||
|
l.client.Call("Plugin.Debug", cArgs, &struct{}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerClient) Info(msg string, args ...interface{}) {
|
||||||
|
cArgs := &LoggerArgs{
|
||||||
|
Msg: msg,
|
||||||
|
Args: args,
|
||||||
|
}
|
||||||
|
l.client.Call("Plugin.Info", cArgs, &struct{}{})
|
||||||
|
}
|
||||||
|
func (l *LoggerClient) Warn(msg string, args ...interface{}) error {
|
||||||
|
var reply LoggerReply
|
||||||
|
cArgs := &LoggerArgs{
|
||||||
|
Msg: msg,
|
||||||
|
Args: args,
|
||||||
|
}
|
||||||
|
err := l.client.Call("Plugin.Warn", cArgs, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if reply.Error != nil {
|
||||||
|
return reply.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (l *LoggerClient) Error(msg string, args ...interface{}) error {
|
||||||
|
var reply LoggerReply
|
||||||
|
cArgs := &LoggerArgs{
|
||||||
|
Msg: msg,
|
||||||
|
Args: args,
|
||||||
|
}
|
||||||
|
err := l.client.Call("Plugin.Error", cArgs, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if reply.Error != nil {
|
||||||
|
return reply.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerClient) Fatal(msg string, args ...interface{}) {
|
||||||
|
// NOOP since it's not actually used within vault
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerClient) Log(level int, msg string, args []interface{}) {
|
||||||
|
cArgs := &LoggerArgs{
|
||||||
|
Level: level,
|
||||||
|
Msg: msg,
|
||||||
|
Args: args,
|
||||||
|
}
|
||||||
|
l.client.Call("Plugin.Log", cArgs, &struct{}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerClient) SetLevel(level int) {
|
||||||
|
l.client.Call("Plugin.SetLevel", level, &struct{}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerClient) IsTrace() bool {
|
||||||
|
var reply LoggerReply
|
||||||
|
l.client.Call("Plugin.IsTrace", new(interface{}), &reply)
|
||||||
|
return reply.IsTrue
|
||||||
|
}
|
||||||
|
func (l *LoggerClient) IsDebug() bool {
|
||||||
|
var reply LoggerReply
|
||||||
|
l.client.Call("Plugin.IsDebug", new(interface{}), &reply)
|
||||||
|
return reply.IsTrue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerClient) IsInfo() bool {
|
||||||
|
var reply LoggerReply
|
||||||
|
l.client.Call("Plugin.IsInfo", new(interface{}), &reply)
|
||||||
|
return reply.IsTrue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerClient) IsWarn() bool {
|
||||||
|
var reply LoggerReply
|
||||||
|
l.client.Call("Plugin.IsWarn", new(interface{}), &reply)
|
||||||
|
return reply.IsTrue
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoggerServer struct {
|
||||||
|
logger log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerServer) Trace(args *LoggerArgs, _ *struct{}) error {
|
||||||
|
l.logger.Trace(args.Msg, args.Args)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerServer) Debug(args *LoggerArgs, _ *struct{}) error {
|
||||||
|
l.logger.Debug(args.Msg, args.Args)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerServer) Info(args *LoggerArgs, _ *struct{}) error {
|
||||||
|
l.logger.Info(args.Msg, args.Args)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerServer) Warn(args *LoggerArgs, reply *LoggerReply) error {
|
||||||
|
err := l.logger.Warn(args.Msg, args.Args)
|
||||||
|
if err != nil {
|
||||||
|
*reply = LoggerReply{
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerServer) Error(args *LoggerArgs, reply *LoggerReply) error {
|
||||||
|
err := l.logger.Error(args.Msg, args.Args)
|
||||||
|
if err != nil {
|
||||||
|
*reply = LoggerReply{
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerServer) Log(args *LoggerArgs, _ *struct{}) error {
|
||||||
|
l.logger.Log(args.Level, args.Msg, args.Args)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerServer) SetLevel(args int, _ *struct{}) error {
|
||||||
|
l.logger.SetLevel(args)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerServer) IsTrace(args interface{}, reply *LoggerReply) error {
|
||||||
|
result := l.logger.IsTrace()
|
||||||
|
*reply = LoggerReply{
|
||||||
|
IsTrue: result,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerServer) IsDebug(args interface{}, reply *LoggerReply) error {
|
||||||
|
result := l.logger.IsDebug()
|
||||||
|
*reply = LoggerReply{
|
||||||
|
IsTrue: result,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerServer) IsInfo(args interface{}, reply *LoggerReply) error {
|
||||||
|
result := l.logger.IsInfo()
|
||||||
|
*reply = LoggerReply{
|
||||||
|
IsTrue: result,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LoggerServer) IsWarn(args interface{}, reply *LoggerReply) error {
|
||||||
|
result := l.logger.IsWarn()
|
||||||
|
*reply = LoggerReply{
|
||||||
|
IsTrue: result,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoggerArgs struct {
|
||||||
|
Level int
|
||||||
|
Msg string
|
||||||
|
Args []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoggerReply contains the RPC reply. Not all fields may be used
|
||||||
|
// for a particular RPC call.
|
||||||
|
type LoggerReply struct {
|
||||||
|
IsTrue bool
|
||||||
|
Error *plugin.BasicError
|
||||||
|
}
|
||||||
163
logical/plugin/logger_test.go
Normal file
163
logical/plugin/logger_test.go
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
plugin "github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/vault/helper/logformat"
|
||||||
|
log "github.com/mgutz/logxi/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLogger_impl(t *testing.T) {
|
||||||
|
var _ log.Logger = new(LoggerClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger_levels(t *testing.T) {
|
||||||
|
client, server := plugin.TestRPCConn(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
writer := bufio.NewWriter(&buf)
|
||||||
|
|
||||||
|
l := logformat.NewVaultLoggerWithWriter(writer, log.LevelTrace)
|
||||||
|
|
||||||
|
server.RegisterName("Plugin", &LoggerServer{
|
||||||
|
logger: l,
|
||||||
|
})
|
||||||
|
|
||||||
|
expected := "foobar"
|
||||||
|
testLogger := &LoggerClient{client: client}
|
||||||
|
|
||||||
|
// Test trace
|
||||||
|
testLogger.Trace(expected)
|
||||||
|
if err := writer.Flush(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
result := buf.String()
|
||||||
|
buf.Reset()
|
||||||
|
if !strings.Contains(result, expected) {
|
||||||
|
t.Fatalf("expected log to contain %s, got %s", expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test debug
|
||||||
|
testLogger.Debug(expected)
|
||||||
|
if err := writer.Flush(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
result = buf.String()
|
||||||
|
buf.Reset()
|
||||||
|
if !strings.Contains(result, expected) {
|
||||||
|
t.Fatalf("expected log to contain %s, got %s", expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test debug
|
||||||
|
testLogger.Info(expected)
|
||||||
|
if err := writer.Flush(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
result = buf.String()
|
||||||
|
buf.Reset()
|
||||||
|
if !strings.Contains(result, expected) {
|
||||||
|
t.Fatalf("expected log to contain %s, got %s", expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test warn
|
||||||
|
testLogger.Warn(expected)
|
||||||
|
if err := writer.Flush(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
result = buf.String()
|
||||||
|
buf.Reset()
|
||||||
|
if !strings.Contains(result, expected) {
|
||||||
|
t.Fatalf("expected log to contain %s, got %s", expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test error
|
||||||
|
testLogger.Error(expected)
|
||||||
|
if err := writer.Flush(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
result = buf.String()
|
||||||
|
buf.Reset()
|
||||||
|
if !strings.Contains(result, expected) {
|
||||||
|
t.Fatalf("expected log to contain %s, got %s", expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test fatal
|
||||||
|
testLogger.Fatal(expected)
|
||||||
|
if err := writer.Flush(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
result = buf.String()
|
||||||
|
buf.Reset()
|
||||||
|
if result != "" {
|
||||||
|
t.Fatalf("expected log Fatal() to be no-op, got %s", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger_isLevels(t *testing.T) {
|
||||||
|
client, server := plugin.TestRPCConn(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
l := logformat.NewVaultLoggerWithWriter(ioutil.Discard, log.LevelAll)
|
||||||
|
|
||||||
|
server.RegisterName("Plugin", &LoggerServer{
|
||||||
|
logger: l,
|
||||||
|
})
|
||||||
|
|
||||||
|
testLogger := &LoggerClient{client: client}
|
||||||
|
|
||||||
|
if !testLogger.IsDebug() || !testLogger.IsInfo() || !testLogger.IsTrace() || !testLogger.IsWarn() {
|
||||||
|
t.Fatal("expected logger to return true for all logger level checks")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger_log(t *testing.T) {
|
||||||
|
client, server := plugin.TestRPCConn(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
writer := bufio.NewWriter(&buf)
|
||||||
|
|
||||||
|
l := logformat.NewVaultLoggerWithWriter(writer, log.LevelTrace)
|
||||||
|
|
||||||
|
server.RegisterName("Plugin", &LoggerServer{
|
||||||
|
logger: l,
|
||||||
|
})
|
||||||
|
|
||||||
|
expected := "foobar"
|
||||||
|
testLogger := &LoggerClient{client: client}
|
||||||
|
|
||||||
|
// Test trace
|
||||||
|
testLogger.Log(log.LevelInfo, expected, nil)
|
||||||
|
if err := writer.Flush(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
result := buf.String()
|
||||||
|
if !strings.Contains(result, expected) {
|
||||||
|
t.Fatalf("expected log to contain %s, got %s", expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger_setLevel(t *testing.T) {
|
||||||
|
client, server := plugin.TestRPCConn(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
l := log.NewLogger(ioutil.Discard, "test-logger")
|
||||||
|
|
||||||
|
server.RegisterName("Plugin", &LoggerServer{
|
||||||
|
logger: l,
|
||||||
|
})
|
||||||
|
|
||||||
|
testLogger := &LoggerClient{client: client}
|
||||||
|
testLogger.SetLevel(log.LevelWarn)
|
||||||
|
|
||||||
|
if !testLogger.IsWarn() {
|
||||||
|
t.Fatal("expected logger to support warn level")
|
||||||
|
}
|
||||||
|
}
|
||||||
69
logical/plugin/mock/backend.go
Normal file
69
logical/plugin/mock/backend.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
"github.com/hashicorp/vault/logical/framework"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns a new backend as an interface. This func
|
||||||
|
// is only necessary for builtin backend plugins.
|
||||||
|
func New() (interface{}, error) {
|
||||||
|
return Backend(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factory returns a new backend as logical.Backend.
|
||||||
|
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
|
b := Backend()
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FactoryType is a wrapper func that allows the Factory func to specify
|
||||||
|
// the backend type for the mock backend plugin instance.
|
||||||
|
func FactoryType(backendType logical.BackendType) func(*logical.BackendConfig) (logical.Backend, error) {
|
||||||
|
return func(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
|
b := Backend()
|
||||||
|
b.BackendType = backendType
|
||||||
|
if err := b.Setup(conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backend returns a private embedded struct of framework.Backend.
|
||||||
|
func Backend() *backend {
|
||||||
|
var b backend
|
||||||
|
b.Backend = &framework.Backend{
|
||||||
|
Help: "",
|
||||||
|
Paths: []*framework.Path{
|
||||||
|
pathTesting(&b),
|
||||||
|
pathInternal(&b),
|
||||||
|
},
|
||||||
|
PathsSpecial: &logical.Paths{
|
||||||
|
Unauthenticated: []string{
|
||||||
|
"special",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Secrets: []*framework.Secret{},
|
||||||
|
Invalidate: b.invalidate,
|
||||||
|
}
|
||||||
|
b.internal = "bar"
|
||||||
|
return &b
|
||||||
|
}
|
||||||
|
|
||||||
|
type backend struct {
|
||||||
|
*framework.Backend
|
||||||
|
|
||||||
|
// internal is used to test invalidate
|
||||||
|
internal string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) invalidate(key string) {
|
||||||
|
switch key {
|
||||||
|
case "internal":
|
||||||
|
b.internal = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
11
logical/plugin/mock/backend_test.go
Normal file
11
logical/plugin/mock/backend_test.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMockBackend_impl(t *testing.T) {
|
||||||
|
var _ logical.Backend = new(backend)
|
||||||
|
}
|
||||||
28
logical/plugin/mock/mock-plugin/main.go
Normal file
28
logical/plugin/mock/mock-plugin/main.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/helper/pluginutil"
|
||||||
|
"github.com/hashicorp/vault/logical/plugin"
|
||||||
|
"github.com/hashicorp/vault/logical/plugin/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
apiClientMeta := &pluginutil.APIClientMeta{}
|
||||||
|
flags := apiClientMeta.FlagSet()
|
||||||
|
flags.Parse(os.Args)
|
||||||
|
|
||||||
|
tlsConfig := apiClientMeta.GetTLSConfig()
|
||||||
|
tlsProviderFunc := pluginutil.VaultPluginTLSProvider(tlsConfig)
|
||||||
|
|
||||||
|
err := plugin.Serve(&plugin.ServeOpts{
|
||||||
|
BackendFactoryFunc: mock.Factory,
|
||||||
|
TLSProviderFunc: tlsProviderFunc,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
28
logical/plugin/mock/path_internal.go
Normal file
28
logical/plugin/mock/path_internal.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
"github.com/hashicorp/vault/logical/framework"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pathInternal(b *backend) *framework.Path {
|
||||||
|
return &framework.Path{
|
||||||
|
Pattern: "internal",
|
||||||
|
Fields: map[string]*framework.FieldSchema{},
|
||||||
|
ExistenceCheck: b.pathTestingExistenceCheck,
|
||||||
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
|
logical.ReadOperation: b.pathTestingReadInternal,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathTestingReadInternal(
|
||||||
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
// Return the secret
|
||||||
|
return &logical.Response{
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"value": b.internal,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
}
|
||||||
56
logical/plugin/mock/path_testing.go
Normal file
56
logical/plugin/mock/path_testing.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
"github.com/hashicorp/vault/logical/framework"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pathTesting(b *backend) *framework.Path {
|
||||||
|
return &framework.Path{
|
||||||
|
Pattern: "test/ing",
|
||||||
|
Fields: map[string]*framework.FieldSchema{
|
||||||
|
"value": &framework.FieldSchema{Type: framework.TypeString},
|
||||||
|
},
|
||||||
|
ExistenceCheck: b.pathTestingExistenceCheck,
|
||||||
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
|
logical.ReadOperation: b.pathTestingRead,
|
||||||
|
logical.CreateOperation: b.pathTestingCreate,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathTestingRead(
|
||||||
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
// Return the secret
|
||||||
|
return &logical.Response{
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"value": data.Get("value"),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathTestingCreate(
|
||||||
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
val := data.Get("value").(string)
|
||||||
|
|
||||||
|
entry := &logical.StorageEntry{
|
||||||
|
Key: "test/ing",
|
||||||
|
Value: []byte(val),
|
||||||
|
}
|
||||||
|
|
||||||
|
s := req.Storage
|
||||||
|
err := s.Put(entry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &logical.Response{
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"value": data.Get("value"),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathTestingExistenceCheck(req *logical.Request, data *framework.FieldData) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
96
logical/plugin/plugin.go
Normal file
96
logical/plugin/plugin.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/vault/helper/pluginutil"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackendPluginClient is a wrapper around backendPluginClient
|
||||||
|
// that also contains its plugin.Client instance. It's primarily
|
||||||
|
// used to cleanly kill the client on Cleanup()
|
||||||
|
type BackendPluginClient struct {
|
||||||
|
client *plugin.Client
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
*backendPluginClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup calls the RPC client's Cleanup() func and also calls
|
||||||
|
// the go-plugin's client Kill() func
|
||||||
|
func (b *BackendPluginClient) Cleanup() {
|
||||||
|
b.backendPluginClient.Cleanup()
|
||||||
|
b.client.Kill()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackend will return an instance of an RPC-based client implementation of the backend for
|
||||||
|
// external plugins, or a concrete implementation of the backend if it is a builtin backend.
|
||||||
|
// The backend is returned as a logical.Backend interface.
|
||||||
|
func NewBackend(pluginName string, sys pluginutil.LookRunnerUtil) (logical.Backend, error) {
|
||||||
|
// Look for plugin in the plugin catalog
|
||||||
|
pluginRunner, err := sys.LookupPlugin(pluginName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var backend logical.Backend
|
||||||
|
if pluginRunner.Builtin {
|
||||||
|
// Plugin is builtin so we can retrieve an instance of the interface
|
||||||
|
// from the pluginRunner. Then cast it to logical.Backend.
|
||||||
|
backendRaw, err := pluginRunner.BuiltinFactory()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting plugin type: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
backend, ok = backendRaw.(logical.Backend)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unsuported backend type: %s", pluginName)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// create a backendPluginClient instance
|
||||||
|
backend, err = newPluginClient(sys, pluginRunner)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return backend, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPluginClient(sys pluginutil.RunnerUtil, pluginRunner *pluginutil.PluginRunner) (logical.Backend, error) {
|
||||||
|
// pluginMap is the map of plugins we can dispense.
|
||||||
|
pluginMap := map[string]plugin.Plugin{
|
||||||
|
"backend": &BackendPlugin{},
|
||||||
|
}
|
||||||
|
client, err := pluginRunner.Run(sys, pluginMap, handshakeConfig, []string{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect via RPC
|
||||||
|
rpcClient, err := client.Client()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request the plugin
|
||||||
|
raw, err := rpcClient.Dispense("backend")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should have a logical backend type now. This feels like a normal interface
|
||||||
|
// implementation but is in fact over an RPC connection.
|
||||||
|
backendRPC := raw.(*backendPluginClient)
|
||||||
|
|
||||||
|
return &BackendPluginClient{
|
||||||
|
client: client,
|
||||||
|
backendPluginClient: backendRPC,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
54
logical/plugin/serve.go
Normal file
54
logical/plugin/serve.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/vault/helper/pluginutil"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackendPluginName is the name of the plugin that can be
|
||||||
|
// dispensed rom the plugin server.
|
||||||
|
const BackendPluginName = "backend"
|
||||||
|
|
||||||
|
type BackendFactoryFunc func(*logical.BackendConfig) (logical.Backend, error)
|
||||||
|
type TLSProdiverFunc func() (*tls.Config, error)
|
||||||
|
|
||||||
|
type ServeOpts struct {
|
||||||
|
BackendFactoryFunc BackendFactoryFunc
|
||||||
|
TLSProviderFunc TLSProdiverFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve is used to serve a backend plugin
|
||||||
|
func Serve(opts *ServeOpts) error {
|
||||||
|
// pluginMap is the map of plugins we can dispense.
|
||||||
|
var pluginMap = map[string]plugin.Plugin{
|
||||||
|
"backend": &BackendPlugin{
|
||||||
|
Factory: opts.BackendFactoryFunc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := pluginutil.OptionallyEnableMlock()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.Serve(&plugin.ServeConfig{
|
||||||
|
HandshakeConfig: handshakeConfig,
|
||||||
|
Plugins: pluginMap,
|
||||||
|
TLSProvider: opts.TLSProviderFunc,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// handshakeConfigs are used to just do a basic handshake between
|
||||||
|
// a plugin and host. If the handshake fails, a user friendly error is shown.
|
||||||
|
// This prevents users from executing bad plugins or executing a plugin
|
||||||
|
// directory. It is a UX feature, not a security feature.
|
||||||
|
var handshakeConfig = plugin.HandshakeConfig{
|
||||||
|
ProtocolVersion: 1,
|
||||||
|
MagicCookieKey: "VAULT_BACKEND_PLUGIN",
|
||||||
|
MagicCookieValue: "6669da05-b1c8-4f49-97d9-c8e5bed98e20",
|
||||||
|
}
|
||||||
119
logical/plugin/storage.go
Normal file
119
logical/plugin/storage.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/rpc"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StorageClient is an implementation of logical.Storage that communicates
|
||||||
|
// over RPC.
|
||||||
|
type StorageClient struct {
|
||||||
|
client *rpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageClient) List(prefix string) ([]string, error) {
|
||||||
|
var reply StorageListReply
|
||||||
|
err := s.client.Call("Plugin.List", prefix, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return reply.Keys, err
|
||||||
|
}
|
||||||
|
if reply.Error != nil {
|
||||||
|
return reply.Keys, reply.Error
|
||||||
|
}
|
||||||
|
return reply.Keys, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageClient) Get(key string) (*logical.StorageEntry, error) {
|
||||||
|
var reply StorageGetReply
|
||||||
|
err := s.client.Call("Plugin.Get", key, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if reply.Error != nil {
|
||||||
|
return nil, reply.Error
|
||||||
|
}
|
||||||
|
return reply.StorageEntry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageClient) Put(entry *logical.StorageEntry) error {
|
||||||
|
var reply StoragePutReply
|
||||||
|
err := s.client.Call("Plugin.Put", entry, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if reply.Error != nil {
|
||||||
|
return reply.Error
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageClient) Delete(key string) error {
|
||||||
|
var reply StorageDeleteReply
|
||||||
|
err := s.client.Call("Plugin.Delete", key, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if reply.Error != nil {
|
||||||
|
return reply.Error
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageServer is a net/rpc compatible structure for serving
|
||||||
|
type StorageServer struct {
|
||||||
|
impl logical.Storage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageServer) List(prefix string, reply *StorageListReply) error {
|
||||||
|
keys, err := s.impl.List(prefix)
|
||||||
|
*reply = StorageListReply{
|
||||||
|
Keys: keys,
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageServer) Get(key string, reply *StorageGetReply) error {
|
||||||
|
storageEntry, err := s.impl.Get(key)
|
||||||
|
*reply = StorageGetReply{
|
||||||
|
StorageEntry: storageEntry,
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageServer) Put(entry *logical.StorageEntry, reply *StoragePutReply) error {
|
||||||
|
err := s.impl.Put(entry)
|
||||||
|
*reply = StoragePutReply{
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageServer) Delete(key string, reply *StorageDeleteReply) error {
|
||||||
|
err := s.impl.Delete(key)
|
||||||
|
*reply = StorageDeleteReply{
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type StorageListReply struct {
|
||||||
|
Keys []string
|
||||||
|
Error *plugin.BasicError
|
||||||
|
}
|
||||||
|
|
||||||
|
type StorageGetReply struct {
|
||||||
|
StorageEntry *logical.StorageEntry
|
||||||
|
Error *plugin.BasicError
|
||||||
|
}
|
||||||
|
|
||||||
|
type StoragePutReply struct {
|
||||||
|
Error *plugin.BasicError
|
||||||
|
}
|
||||||
|
|
||||||
|
type StorageDeleteReply struct {
|
||||||
|
Error *plugin.BasicError
|
||||||
|
}
|
||||||
27
logical/plugin/storage_test.go
Normal file
27
logical/plugin/storage_test.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
plugin "github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStorage_impl(t *testing.T) {
|
||||||
|
var _ logical.Storage = new(StorageClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStorage_operations(t *testing.T) {
|
||||||
|
client, server := plugin.TestRPCConn(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
storage := &logical.InmemStorage{}
|
||||||
|
|
||||||
|
server.RegisterName("Plugin", &StorageServer{
|
||||||
|
impl: storage,
|
||||||
|
})
|
||||||
|
|
||||||
|
testStorage := &StorageClient{client: client}
|
||||||
|
|
||||||
|
logical.TestStorage(t, testStorage)
|
||||||
|
}
|
||||||
247
logical/plugin/system.go
Normal file
247
logical/plugin/system.go
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/rpc"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
plugin "github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/vault/helper/consts"
|
||||||
|
"github.com/hashicorp/vault/helper/pluginutil"
|
||||||
|
"github.com/hashicorp/vault/helper/wrapping"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SystemViewClient struct {
|
||||||
|
client *rpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewClient) DefaultLeaseTTL() time.Duration {
|
||||||
|
var reply DefaultLeaseTTLReply
|
||||||
|
err := s.client.Call("Plugin.DefaultLeaseTTL", new(interface{}), &reply)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.DefaultLeaseTTL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewClient) MaxLeaseTTL() time.Duration {
|
||||||
|
var reply MaxLeaseTTLReply
|
||||||
|
err := s.client.Call("Plugin.MaxLeaseTTL", new(interface{}), &reply)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.MaxLeaseTTL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewClient) SudoPrivilege(path string, token string) bool {
|
||||||
|
var reply SudoPrivilegeReply
|
||||||
|
args := &SudoPrivilegeArgs{
|
||||||
|
Path: path,
|
||||||
|
Token: token,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.client.Call("Plugin.SudoPrivilege", args, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.Sudo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewClient) Tainted() bool {
|
||||||
|
var reply TaintedReply
|
||||||
|
|
||||||
|
err := s.client.Call("Plugin.Tainted", new(interface{}), &reply)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.Tainted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewClient) CachingDisabled() bool {
|
||||||
|
var reply CachingDisabledReply
|
||||||
|
|
||||||
|
err := s.client.Call("Plugin.CachingDisabled", new(interface{}), &reply)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.CachingDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewClient) ReplicationState() consts.ReplicationState {
|
||||||
|
var reply ReplicationStateReply
|
||||||
|
|
||||||
|
err := s.client.Call("Plugin.ReplicationState", new(interface{}), &reply)
|
||||||
|
if err != nil {
|
||||||
|
return consts.ReplicationDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.ReplicationState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewClient) ResponseWrapData(data map[string]interface{}, ttl time.Duration, jwt bool) (*wrapping.ResponseWrapInfo, error) {
|
||||||
|
var reply ResponseWrapDataReply
|
||||||
|
// Do not allow JWTs to be returned
|
||||||
|
args := &ResponseWrapDataArgs{
|
||||||
|
Data: data,
|
||||||
|
TTL: ttl,
|
||||||
|
JWT: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.client.Call("Plugin.ResponseWrapData", args, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if reply.Error != nil {
|
||||||
|
return nil, reply.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.ResponseWrapInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewClient) LookupPlugin(name string) (*pluginutil.PluginRunner, error) {
|
||||||
|
return nil, fmt.Errorf("cannot call LookupPlugin from a plugin backend")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewClient) MlockEnabled() bool {
|
||||||
|
var reply MlockEnabledReply
|
||||||
|
err := s.client.Call("Plugin.MlockEnabled", new(interface{}), &reply)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.MlockEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemViewServer struct {
|
||||||
|
impl logical.SystemView
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewServer) DefaultLeaseTTL(_ interface{}, reply *DefaultLeaseTTLReply) error {
|
||||||
|
ttl := s.impl.DefaultLeaseTTL()
|
||||||
|
*reply = DefaultLeaseTTLReply{
|
||||||
|
DefaultLeaseTTL: ttl,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewServer) MaxLeaseTTL(_ interface{}, reply *MaxLeaseTTLReply) error {
|
||||||
|
ttl := s.impl.MaxLeaseTTL()
|
||||||
|
*reply = MaxLeaseTTLReply{
|
||||||
|
MaxLeaseTTL: ttl,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewServer) SudoPrivilege(args *SudoPrivilegeArgs, reply *SudoPrivilegeReply) error {
|
||||||
|
sudo := s.impl.SudoPrivilege(args.Path, args.Token)
|
||||||
|
*reply = SudoPrivilegeReply{
|
||||||
|
Sudo: sudo,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewServer) Tainted(_ interface{}, reply *TaintedReply) error {
|
||||||
|
tainted := s.impl.Tainted()
|
||||||
|
*reply = TaintedReply{
|
||||||
|
Tainted: tainted,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewServer) CachingDisabled(_ interface{}, reply *CachingDisabledReply) error {
|
||||||
|
cachingDisabled := s.impl.CachingDisabled()
|
||||||
|
*reply = CachingDisabledReply{
|
||||||
|
CachingDisabled: cachingDisabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewServer) ReplicationState(_ interface{}, reply *ReplicationStateReply) error {
|
||||||
|
replicationState := s.impl.ReplicationState()
|
||||||
|
*reply = ReplicationStateReply{
|
||||||
|
ReplicationState: replicationState,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewServer) ResponseWrapData(args *ResponseWrapDataArgs, reply *ResponseWrapDataReply) error {
|
||||||
|
// Do not allow JWTs to be returned
|
||||||
|
info, err := s.impl.ResponseWrapData(args.Data, args.TTL, false)
|
||||||
|
if err != nil {
|
||||||
|
*reply = ResponseWrapDataReply{
|
||||||
|
Error: plugin.NewBasicError(err),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
*reply = ResponseWrapDataReply{
|
||||||
|
ResponseWrapInfo: info,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemViewServer) MlockEnabled(_ interface{}, reply *MlockEnabledReply) error {
|
||||||
|
enabled := s.impl.MlockEnabled()
|
||||||
|
*reply = MlockEnabledReply{
|
||||||
|
MlockEnabled: enabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultLeaseTTLReply struct {
|
||||||
|
DefaultLeaseTTL time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type MaxLeaseTTLReply struct {
|
||||||
|
MaxLeaseTTL time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type SudoPrivilegeArgs struct {
|
||||||
|
Path string
|
||||||
|
Token string
|
||||||
|
}
|
||||||
|
|
||||||
|
type SudoPrivilegeReply struct {
|
||||||
|
Sudo bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type TaintedReply struct {
|
||||||
|
Tainted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type CachingDisabledReply struct {
|
||||||
|
CachingDisabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReplicationStateReply struct {
|
||||||
|
ReplicationState consts.ReplicationState
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResponseWrapDataArgs struct {
|
||||||
|
Data map[string]interface{}
|
||||||
|
TTL time.Duration
|
||||||
|
JWT bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResponseWrapDataReply struct {
|
||||||
|
ResponseWrapInfo *wrapping.ResponseWrapInfo
|
||||||
|
Error *plugin.BasicError
|
||||||
|
}
|
||||||
|
|
||||||
|
type MlockEnabledReply struct {
|
||||||
|
MlockEnabled bool
|
||||||
|
}
|
||||||
174
logical/plugin/system_test.go
Normal file
174
logical/plugin/system_test.go
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
plugin "github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/vault/helper/consts"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_impl(t *testing.T) {
|
||||||
|
var _ logical.SystemView = new(SystemViewClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSystem_defaultLeaseTTL(t *testing.T) {
|
||||||
|
client, server := plugin.TestRPCConn(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
sys := logical.TestSystemView()
|
||||||
|
|
||||||
|
server.RegisterName("Plugin", &SystemViewServer{
|
||||||
|
impl: sys,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSystemView := &SystemViewClient{client: client}
|
||||||
|
|
||||||
|
expected := sys.DefaultLeaseTTL()
|
||||||
|
actual := testSystemView.DefaultLeaseTTL()
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Fatalf("expected: %v, got: %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSystem_maxLeaseTTL(t *testing.T) {
|
||||||
|
client, server := plugin.TestRPCConn(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
sys := logical.TestSystemView()
|
||||||
|
|
||||||
|
server.RegisterName("Plugin", &SystemViewServer{
|
||||||
|
impl: sys,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSystemView := &SystemViewClient{client: client}
|
||||||
|
|
||||||
|
expected := sys.MaxLeaseTTL()
|
||||||
|
actual := testSystemView.MaxLeaseTTL()
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Fatalf("expected: %v, got: %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSystem_sudoPrivilege(t *testing.T) {
|
||||||
|
client, server := plugin.TestRPCConn(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
sys := logical.TestSystemView()
|
||||||
|
sys.SudoPrivilegeVal = true
|
||||||
|
|
||||||
|
server.RegisterName("Plugin", &SystemViewServer{
|
||||||
|
impl: sys,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSystemView := &SystemViewClient{client: client}
|
||||||
|
|
||||||
|
expected := sys.SudoPrivilege("foo", "bar")
|
||||||
|
actual := testSystemView.SudoPrivilege("foo", "bar")
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Fatalf("expected: %v, got: %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSystem_tainted(t *testing.T) {
|
||||||
|
client, server := plugin.TestRPCConn(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
sys := logical.TestSystemView()
|
||||||
|
sys.TaintedVal = true
|
||||||
|
|
||||||
|
server.RegisterName("Plugin", &SystemViewServer{
|
||||||
|
impl: sys,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSystemView := &SystemViewClient{client: client}
|
||||||
|
|
||||||
|
expected := sys.Tainted()
|
||||||
|
actual := testSystemView.Tainted()
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Fatalf("expected: %v, got: %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSystem_cachingDisabled(t *testing.T) {
|
||||||
|
client, server := plugin.TestRPCConn(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
sys := logical.TestSystemView()
|
||||||
|
sys.CachingDisabledVal = true
|
||||||
|
|
||||||
|
server.RegisterName("Plugin", &SystemViewServer{
|
||||||
|
impl: sys,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSystemView := &SystemViewClient{client: client}
|
||||||
|
|
||||||
|
expected := sys.CachingDisabled()
|
||||||
|
actual := testSystemView.CachingDisabled()
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Fatalf("expected: %v, got: %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSystem_replicationState(t *testing.T) {
|
||||||
|
client, server := plugin.TestRPCConn(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
sys := logical.TestSystemView()
|
||||||
|
sys.ReplicationStateVal = consts.ReplicationPrimary
|
||||||
|
|
||||||
|
server.RegisterName("Plugin", &SystemViewServer{
|
||||||
|
impl: sys,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSystemView := &SystemViewClient{client: client}
|
||||||
|
|
||||||
|
expected := sys.ReplicationState()
|
||||||
|
actual := testSystemView.ReplicationState()
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Fatalf("expected: %v, got: %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSystem_responseWrapData(t *testing.T) {
|
||||||
|
t.SkipNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSystem_lookupPlugin(t *testing.T) {
|
||||||
|
client, server := plugin.TestRPCConn(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
sys := logical.TestSystemView()
|
||||||
|
|
||||||
|
server.RegisterName("Plugin", &SystemViewServer{
|
||||||
|
impl: sys,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSystemView := &SystemViewClient{client: client}
|
||||||
|
|
||||||
|
if _, err := testSystemView.LookupPlugin("foo"); err == nil {
|
||||||
|
t.Fatal("LookPlugin(): expected error on due to unsupported call from plugin")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSystem_mlockEnabled(t *testing.T) {
|
||||||
|
client, server := plugin.TestRPCConn(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
sys := logical.TestSystemView()
|
||||||
|
sys.EnableMlock = true
|
||||||
|
|
||||||
|
server.RegisterName("Plugin", &SystemViewServer{
|
||||||
|
impl: sys,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSystemView := &SystemViewClient{client: client}
|
||||||
|
|
||||||
|
expected := sys.MlockEnabled()
|
||||||
|
actual := testSystemView.MlockEnabled()
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Fatalf("expected: %v, got: %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,9 +95,13 @@ func (c *Core) enableCredential(entry *MountEntry) error {
|
|||||||
viewPath := credentialBarrierPrefix + entry.UUID + "/"
|
viewPath := credentialBarrierPrefix + entry.UUID + "/"
|
||||||
view := NewBarrierView(c.barrier, viewPath)
|
view := NewBarrierView(c.barrier, viewPath)
|
||||||
sysView := c.mountEntrySysView(entry)
|
sysView := c.mountEntrySysView(entry)
|
||||||
|
conf := make(map[string]string)
|
||||||
|
if entry.Config.PluginName != "" {
|
||||||
|
conf["plugin_name"] = entry.Config.PluginName
|
||||||
|
}
|
||||||
|
|
||||||
// Create the new backend
|
// Create the new backend
|
||||||
backend, err := c.newCredentialBackend(entry.Type, sysView, view, nil)
|
backend, err := c.newCredentialBackend(entry.Type, sysView, view, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -105,6 +109,12 @@ func (c *Core) enableCredential(entry *MountEntry) error {
|
|||||||
return fmt.Errorf("nil backend returned from %q factory", entry.Type)
|
return fmt.Errorf("nil backend returned from %q factory", entry.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for the correct backend type
|
||||||
|
backendType := backend.Type()
|
||||||
|
if entry.Type == "plugin" && backendType != logical.TypeCredential {
|
||||||
|
return fmt.Errorf("cannot mount '%s' of type '%s' as an auth backend", entry.Config.PluginName, backendType)
|
||||||
|
}
|
||||||
|
|
||||||
if err := backend.Initialize(); err != nil {
|
if err := backend.Initialize(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -406,9 +416,13 @@ func (c *Core) setupCredentials() error {
|
|||||||
viewPath := credentialBarrierPrefix + entry.UUID + "/"
|
viewPath := credentialBarrierPrefix + entry.UUID + "/"
|
||||||
view = NewBarrierView(c.barrier, viewPath)
|
view = NewBarrierView(c.barrier, viewPath)
|
||||||
sysView := c.mountEntrySysView(entry)
|
sysView := c.mountEntrySysView(entry)
|
||||||
|
conf := make(map[string]string)
|
||||||
|
if entry.Config.PluginName != "" {
|
||||||
|
conf["plugin_name"] = entry.Config.PluginName
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize the backend
|
// Initialize the backend
|
||||||
backend, err = c.newCredentialBackend(entry.Type, sysView, view, nil)
|
backend, err = c.newCredentialBackend(entry.Type, sysView, view, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Error("core: failed to create credential entry", "path", entry.Path, "error", err)
|
c.logger.Error("core: failed to create credential entry", "path", entry.Path, "error", err)
|
||||||
return errLoadAuthFailed
|
return errLoadAuthFailed
|
||||||
@@ -417,6 +431,12 @@ func (c *Core) setupCredentials() error {
|
|||||||
return fmt.Errorf("nil backend returned from %q factory", entry.Type)
|
return fmt.Errorf("nil backend returned from %q factory", entry.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for the correct backend type
|
||||||
|
backendType := backend.Type()
|
||||||
|
if entry.Type == "plugin" && backendType != logical.TypeCredential {
|
||||||
|
return fmt.Errorf("cannot mount '%s' of type '%s' as an auth backend", entry.Config.PluginName, backendType)
|
||||||
|
}
|
||||||
|
|
||||||
if err := backend.Initialize(); err != nil {
|
if err := backend.Initialize(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -516,7 +516,10 @@ func NewCore(conf *CoreConfig) (*Core, error) {
|
|||||||
logicalBackends["cubbyhole"] = CubbyholeBackendFactory
|
logicalBackends["cubbyhole"] = CubbyholeBackendFactory
|
||||||
logicalBackends["system"] = func(config *logical.BackendConfig) (logical.Backend, error) {
|
logicalBackends["system"] = func(config *logical.BackendConfig) (logical.Backend, error) {
|
||||||
b := NewSystemBackend(c)
|
b := NewSystemBackend(c)
|
||||||
return b.Backend.Setup(config)
|
if err := b.Setup(config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
c.logicalBackends = logicalBackends
|
c.logicalBackends = logicalBackends
|
||||||
|
|
||||||
|
|||||||
@@ -1459,5 +1459,10 @@ func badRenewFactory(conf *logical.BackendConfig) (logical.Backend, error) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return be.Setup(conf)
|
err := be.Setup(conf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return be, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ func PassthroughBackendFactory(conf *logical.BackendConfig) (logical.Backend, er
|
|||||||
return LeaseSwitchedPassthroughBackend(conf, false)
|
return LeaseSwitchedPassthroughBackend(conf, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PassthroughBackendWithLeasesFactory returns a PassthroughBackend
|
// LeasedPassthroughBackendFactory returns a PassthroughBackend
|
||||||
// with leases switched on
|
// with leases switched on
|
||||||
func LeasedPassthroughBackendFactory(conf *logical.BackendConfig) (logical.Backend, error) {
|
func LeasedPassthroughBackendFactory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return LeaseSwitchedPassthroughBackend(conf, true)
|
return LeaseSwitchedPassthroughBackend(conf, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LeaseSwitchedPassthroughBackendFactory returns a PassthroughBackend
|
// LeaseSwitchedPassthroughBackend returns a PassthroughBackend
|
||||||
// with leases switched on or off
|
// with leases switched on or off
|
||||||
func LeaseSwitchedPassthroughBackend(conf *logical.BackendConfig, leases bool) (logical.Backend, error) {
|
func LeaseSwitchedPassthroughBackend(conf *logical.BackendConfig, leases bool) (logical.Backend, error) {
|
||||||
var b PassthroughBackend
|
var b PassthroughBackend
|
||||||
@@ -147,6 +147,10 @@ func (b *PassthroughBackend) handleRead(
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *PassthroughBackend) GeneratesLeases() bool {
|
||||||
|
return b.generateLeases
|
||||||
|
}
|
||||||
|
|
||||||
func (b *PassthroughBackend) handleWrite(
|
func (b *PassthroughBackend) handleWrite(
|
||||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
// Check that some fields are given
|
// Check that some fields are given
|
||||||
@@ -202,10 +206,6 @@ func (b *PassthroughBackend) handleList(
|
|||||||
return logical.ListResponse(keys), nil
|
return logical.ListResponse(keys), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *PassthroughBackend) GeneratesLeases() bool {
|
|
||||||
return b.generateLeases
|
|
||||||
}
|
|
||||||
|
|
||||||
const passthroughHelp = `
|
const passthroughHelp = `
|
||||||
The generic backend reads and writes arbitrary secrets to the backend.
|
The generic backend reads and writes arbitrary secrets to the backend.
|
||||||
The secrets are encrypted/decrypted by Vault: they are never stored
|
The secrets are encrypted/decrypted by Vault: they are never stored
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatih/structs"
|
||||||
"github.com/hashicorp/vault/helper/consts"
|
"github.com/hashicorp/vault/helper/consts"
|
||||||
"github.com/hashicorp/vault/helper/parseutil"
|
"github.com/hashicorp/vault/helper/parseutil"
|
||||||
"github.com/hashicorp/vault/helper/wrapping"
|
"github.com/hashicorp/vault/helper/wrapping"
|
||||||
@@ -486,6 +487,10 @@ func NewSystemBackend(core *Core) *SystemBackend {
|
|||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
Description: strings.TrimSpace(sysHelp["auth_desc"][0]),
|
Description: strings.TrimSpace(sysHelp["auth_desc"][0]),
|
||||||
},
|
},
|
||||||
|
"plugin_name": &framework.FieldSchema{
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: strings.TrimSpace(sysHelp["auth_plugin"][0]),
|
||||||
|
},
|
||||||
"local": &framework.FieldSchema{
|
"local": &framework.FieldSchema{
|
||||||
Type: framework.TypeBool,
|
Type: framework.TypeBool,
|
||||||
Default: false,
|
Default: false,
|
||||||
@@ -775,7 +780,7 @@ func NewSystemBackend(core *Core) *SystemBackend {
|
|||||||
HelpDescription: strings.TrimSpace(sysHelp["audited-headers"][1]),
|
HelpDescription: strings.TrimSpace(sysHelp["audited-headers"][1]),
|
||||||
},
|
},
|
||||||
&framework.Path{
|
&framework.Path{
|
||||||
Pattern: "plugins/catalog/$",
|
Pattern: "plugins/catalog/?$",
|
||||||
|
|
||||||
Fields: map[string]*framework.FieldSchema{},
|
Fields: map[string]*framework.FieldSchema{},
|
||||||
|
|
||||||
@@ -792,18 +797,15 @@ func NewSystemBackend(core *Core) *SystemBackend {
|
|||||||
Fields: map[string]*framework.FieldSchema{
|
Fields: map[string]*framework.FieldSchema{
|
||||||
"name": &framework.FieldSchema{
|
"name": &framework.FieldSchema{
|
||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
Description: "The name of the plugin",
|
Description: strings.TrimSpace(sysHelp["plugin-catalog_name"][0]),
|
||||||
},
|
},
|
||||||
"sha_256": &framework.FieldSchema{
|
"sha_256": &framework.FieldSchema{
|
||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
Description: `The SHA256 sum of the executable used in the
|
Description: strings.TrimSpace(sysHelp["plugin-catalog_sha-256"][0]),
|
||||||
command field. This should be HEX encoded.`,
|
|
||||||
},
|
},
|
||||||
"command": &framework.FieldSchema{
|
"command": &framework.FieldSchema{
|
||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
Description: `The command used to start the plugin. The
|
Description: strings.TrimSpace(sysHelp["plugin-catalog_command"][0]),
|
||||||
executable defined in this command must exist in vault's
|
|
||||||
plugin directory.`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -943,10 +945,11 @@ func (b *SystemBackend) handlePluginCatalogRead(req *logical.Request, d *framewo
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a map of data to be returned and remove sensitive information from it
|
||||||
|
data := structs.New(plugin).Map()
|
||||||
|
|
||||||
return &logical.Response{
|
return &logical.Response{
|
||||||
Data: map[string]interface{}{
|
Data: data,
|
||||||
"plugin": plugin,
|
|
||||||
},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1157,18 +1160,17 @@ func (b *SystemBackend) handleMountTable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range b.Core.mounts.Entries {
|
for _, entry := range b.Core.mounts.Entries {
|
||||||
|
// Populate mount info
|
||||||
|
structConfig := structs.New(entry.Config).Map()
|
||||||
|
structConfig["default_lease_ttl"] = int64(structConfig["default_lease_ttl"].(time.Duration).Seconds())
|
||||||
|
structConfig["max_lease_ttl"] = int64(structConfig["max_lease_ttl"].(time.Duration).Seconds())
|
||||||
info := map[string]interface{}{
|
info := map[string]interface{}{
|
||||||
"type": entry.Type,
|
"type": entry.Type,
|
||||||
"description": entry.Description,
|
"description": entry.Description,
|
||||||
"accessor": entry.Accessor,
|
"accessor": entry.Accessor,
|
||||||
"config": map[string]interface{}{
|
"config": structConfig,
|
||||||
"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()),
|
"local": entry.Local,
|
||||||
"max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()),
|
|
||||||
"force_no_cache": entry.Config.ForceNoCache,
|
|
||||||
},
|
|
||||||
"local": entry.Local,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.Data[entry.Path] = info
|
resp.Data[entry.Path] = info
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1195,12 +1197,8 @@ func (b *SystemBackend) handleMount(
|
|||||||
path = sanitizeMountPath(path)
|
path = sanitizeMountPath(path)
|
||||||
|
|
||||||
var config MountConfig
|
var config MountConfig
|
||||||
|
var apiConfig APIMountConfig
|
||||||
|
|
||||||
var apiConfig struct {
|
|
||||||
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
|
||||||
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
|
||||||
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
|
||||||
}
|
|
||||||
configMap := data.Get("config").(map[string]interface{})
|
configMap := data.Get("config").(map[string]interface{})
|
||||||
if configMap != nil && len(configMap) != 0 {
|
if configMap != nil && len(configMap) != 0 {
|
||||||
err := mapstructure.Decode(configMap, &apiConfig)
|
err := mapstructure.Decode(configMap, &apiConfig)
|
||||||
@@ -1249,6 +1247,11 @@ func (b *SystemBackend) handleMount(
|
|||||||
logical.ErrInvalidRequest
|
logical.ErrInvalidRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only set plugin-name if mount is of type plugin
|
||||||
|
if logicalType == "plugin" && apiConfig.PluginName != "" {
|
||||||
|
config.PluginName = apiConfig.PluginName
|
||||||
|
}
|
||||||
|
|
||||||
// Copy over the force no cache if set
|
// Copy over the force no cache if set
|
||||||
if apiConfig.ForceNoCache {
|
if apiConfig.ForceNoCache {
|
||||||
config.ForceNoCache = true
|
config.ForceNoCache = true
|
||||||
@@ -1685,6 +1688,14 @@ func (b *SystemBackend) handleEnableAuth(
|
|||||||
path := data.Get("path").(string)
|
path := data.Get("path").(string)
|
||||||
logicalType := data.Get("type").(string)
|
logicalType := data.Get("type").(string)
|
||||||
description := data.Get("description").(string)
|
description := data.Get("description").(string)
|
||||||
|
pluginName := data.Get("plugin_name").(string)
|
||||||
|
|
||||||
|
var config MountConfig
|
||||||
|
|
||||||
|
// Only set plugin name if mount is of type plugin
|
||||||
|
if logicalType == "plugin" && pluginName != "" {
|
||||||
|
config.PluginName = pluginName
|
||||||
|
}
|
||||||
|
|
||||||
if logicalType == "" {
|
if logicalType == "" {
|
||||||
return logical.ErrorResponse(
|
return logical.ErrorResponse(
|
||||||
@@ -1700,6 +1711,7 @@ func (b *SystemBackend) handleEnableAuth(
|
|||||||
Path: path,
|
Path: path,
|
||||||
Type: logicalType,
|
Type: logicalType,
|
||||||
Description: description,
|
Description: description,
|
||||||
|
Config: config,
|
||||||
Local: local,
|
Local: local,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2585,6 +2597,11 @@ Example: you might have an OAuth backend for GitHub, and one for Google Apps.
|
|||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"auth_plugin": {
|
||||||
|
`Name of the auth plugin to use based from the name in the plugin catalog.`,
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
|
||||||
"policy-list": {
|
"policy-list": {
|
||||||
`List the configured access control policies.`,
|
`List the configured access control policies.`,
|
||||||
`
|
`
|
||||||
@@ -2764,23 +2781,38 @@ This path responds to the following HTTP methods.
|
|||||||
"Lists the headers configured to be audited.",
|
"Lists the headers configured to be audited.",
|
||||||
`Returns a list of headers that have been configured to be audited.`,
|
`Returns a list of headers that have been configured to be audited.`,
|
||||||
},
|
},
|
||||||
"plugins/catalog": {
|
"plugin-catalog": {
|
||||||
`Configures the plugins known to vault`,
|
"Configures the plugins known to vault",
|
||||||
`
|
`
|
||||||
This path responds to the following HTTP methods.
|
This path responds to the following HTTP methods.
|
||||||
LIST /
|
LIST /
|
||||||
Returns a list of names of configured plugins.
|
Returns a list of names of configured plugins.
|
||||||
|
|
||||||
GET /<name>
|
GET /<name>
|
||||||
Retrieve the metadata for the named plugin.
|
Retrieve the metadata for the named plugin.
|
||||||
|
|
||||||
PUT /<name>
|
PUT /<name>
|
||||||
Add or update plugin.
|
Add or update plugin.
|
||||||
|
|
||||||
DELETE /<name>
|
DELETE /<name>
|
||||||
Delete the plugin with the given name.
|
Delete the plugin with the given name.
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
"plugin-catalog_name": {
|
||||||
|
"The name of the plugin",
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
"plugin-catalog_sha-256": {
|
||||||
|
`The SHA256 sum of the executable used in the
|
||||||
|
command field. This should be HEX encoded.`,
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
"plugin-catalog_command": {
|
||||||
|
`The command used to start the plugin. The
|
||||||
|
executable defined in this command must exist in vault's
|
||||||
|
plugin directory.`,
|
||||||
|
"",
|
||||||
|
},
|
||||||
"leases": {
|
"leases": {
|
||||||
`View or list lease metadata.`,
|
`View or list lease metadata.`,
|
||||||
`
|
`
|
||||||
|
|||||||
104
vault/logical_system_integ_test.go
Normal file
104
vault/logical_system_integ_test.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package vault_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/builtin/plugin"
|
||||||
|
"github.com/hashicorp/vault/helper/logformat"
|
||||||
|
"github.com/hashicorp/vault/helper/pluginutil"
|
||||||
|
"github.com/hashicorp/vault/http"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
lplugin "github.com/hashicorp/vault/logical/plugin"
|
||||||
|
"github.com/hashicorp/vault/logical/plugin/mock"
|
||||||
|
"github.com/hashicorp/vault/vault"
|
||||||
|
log "github.com/mgutz/logxi/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSystemBackend_enableAuth_plugin(t *testing.T) {
|
||||||
|
coreConfig := &vault.CoreConfig{
|
||||||
|
CredentialBackends: map[string]logical.Factory{
|
||||||
|
"plugin": plugin.Factory,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster := vault.NewTestCluster(t, coreConfig, true)
|
||||||
|
cluster.StartListeners()
|
||||||
|
defer cluster.CloseListeners()
|
||||||
|
cores := cluster.Cores
|
||||||
|
|
||||||
|
cores[0].Handler.Handle("/", http.Handler(cores[0].Core))
|
||||||
|
cores[1].Handler.Handle("/", http.Handler(cores[1].Core))
|
||||||
|
cores[2].Handler.Handle("/", http.Handler(cores[2].Core))
|
||||||
|
|
||||||
|
core := cores[0]
|
||||||
|
|
||||||
|
b := vault.NewSystemBackend(core.Core)
|
||||||
|
logger := logformat.NewVaultLogger(log.LevelTrace)
|
||||||
|
bc := &logical.BackendConfig{
|
||||||
|
Logger: logger,
|
||||||
|
System: logical.StaticSystemView{
|
||||||
|
DefaultLeaseTTLVal: time.Hour * 24,
|
||||||
|
MaxLeaseTTLVal: time.Hour * 24 * 32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := b.Backend.Setup(bc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMain")
|
||||||
|
|
||||||
|
req := logical.TestRequest(t, logical.UpdateOperation, "auth/mock-plugin")
|
||||||
|
req.Data["type"] = "plugin"
|
||||||
|
req.Data["plugin_name"] = "mock-plugin"
|
||||||
|
|
||||||
|
resp, err := b.HandleRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if resp != nil {
|
||||||
|
t.Fatalf("bad: %v", resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackend_PluginMain(t *testing.T) {
|
||||||
|
if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content := []byte(vault.TestClusterCACert)
|
||||||
|
tmpfile, err := ioutil.TempFile("", "test-cacert")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer os.Remove(tmpfile.Name()) // clean up
|
||||||
|
|
||||||
|
if _, err := tmpfile.Write(content); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tmpfile.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
factoryFunc := mock.FactoryType(logical.TypeCredential)
|
||||||
|
|
||||||
|
args := []string{"--ca-cert=" + tmpfile.Name()}
|
||||||
|
|
||||||
|
apiClientMeta := &pluginutil.APIClientMeta{}
|
||||||
|
flags := apiClientMeta.FlagSet()
|
||||||
|
flags.Parse(args)
|
||||||
|
tlsConfig := apiClientMeta.GetTLSConfig()
|
||||||
|
tlsProviderFunc := pluginutil.VaultPluginTLSProvider(tlsConfig)
|
||||||
|
err = lplugin.Serve(&lplugin.ServeOpts{
|
||||||
|
BackendFactoryFunc: factoryFunc,
|
||||||
|
TLSProviderFunc: tlsProviderFunc,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -985,8 +985,8 @@ func TestSystemBackend_revokePrefixAuth(t *testing.T) {
|
|||||||
MaxLeaseTTLVal: time.Hour * 24 * 32,
|
MaxLeaseTTLVal: time.Hour * 24 * 32,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
be := NewSystemBackend(core)
|
b := NewSystemBackend(core)
|
||||||
b, err := be.Backend.Setup(bc)
|
err := b.Backend.Setup(bc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1049,8 +1049,8 @@ func TestSystemBackend_revokePrefixAuth_origUrl(t *testing.T) {
|
|||||||
MaxLeaseTTLVal: time.Hour * 24 * 32,
|
MaxLeaseTTLVal: time.Hour * 24 * 32,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
be := NewSystemBackend(core)
|
b := NewSystemBackend(core)
|
||||||
b, err := be.Backend.Setup(bc)
|
err := b.Backend.Setup(bc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1591,7 +1591,7 @@ func testSystemBackend(t *testing.T) logical.Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b := NewSystemBackend(c)
|
b := NewSystemBackend(c)
|
||||||
_, err := b.Backend.Setup(bc)
|
err := b.Backend.Setup(bc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1610,7 +1610,7 @@ func testCoreSystemBackend(t *testing.T) (*Core, logical.Backend, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b := NewSystemBackend(c)
|
b := NewSystemBackend(c)
|
||||||
_, err := b.Backend.Setup(bc)
|
err := b.Backend.Setup(bc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1641,22 +1641,16 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
actualRespData := resp.Data
|
||||||
|
|
||||||
expectedBuiltin := &pluginutil.PluginRunner{
|
expectedBuiltin := &pluginutil.PluginRunner{
|
||||||
Name: "mysql-database-plugin",
|
Name: "mysql-database-plugin",
|
||||||
Builtin: true,
|
Builtin: true,
|
||||||
}
|
}
|
||||||
expectedBuiltin.BuiltinFactory, _ = builtinplugins.Get("mysql-database-plugin")
|
expectedRespData := structs.New(expectedBuiltin).Map()
|
||||||
|
|
||||||
p := resp.Data["plugin"].(*pluginutil.PluginRunner)
|
if !reflect.DeepEqual(actualRespData, expectedRespData) {
|
||||||
if &(p.BuiltinFactory) == &(expectedBuiltin.BuiltinFactory) {
|
t.Fatalf("expected did not match actual, got %#v\n expected %#v\n", actualRespData, expectedRespData)
|
||||||
t.Fatal("expected BuiltinFactory did not match actual")
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedBuiltin.BuiltinFactory = nil
|
|
||||||
p.BuiltinFactory = nil
|
|
||||||
if !reflect.DeepEqual(p, expectedBuiltin) {
|
|
||||||
t.Fatalf("expected did not match actual, got %#v\n expected %#v\n", resp.Data["plugin"].(*pluginutil.PluginRunner), expectedBuiltin)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a plugin
|
// Set a plugin
|
||||||
@@ -1680,16 +1674,19 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
actual := resp.Data
|
||||||
|
|
||||||
expected := &pluginutil.PluginRunner{
|
expectedRunner := &pluginutil.PluginRunner{
|
||||||
Name: "test-plugin",
|
Name: "test-plugin",
|
||||||
Command: filepath.Join(sym, filepath.Base(file.Name())),
|
Command: filepath.Join(sym, filepath.Base(file.Name())),
|
||||||
Args: []string{"--test"},
|
Args: []string{"--test"},
|
||||||
Sha256: []byte{'1'},
|
Sha256: []byte{'1'},
|
||||||
Builtin: false,
|
Builtin: false,
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(resp.Data["plugin"].(*pluginutil.PluginRunner), expected) {
|
expected := structs.New(expectedRunner).Map()
|
||||||
t.Fatalf("expected did not match actual, got %#v\n expected %#v\n", resp.Data["plugin"].(*pluginutil.PluginRunner), expected)
|
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Fatalf("expected did not match actual, got %#v\n expected %#v\n", actual, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete plugin
|
// Delete plugin
|
||||||
|
|||||||
@@ -167,6 +167,15 @@ type MountConfig struct {
|
|||||||
DefaultLeaseTTL time.Duration `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` // Override for global default
|
DefaultLeaseTTL time.Duration `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` // Override for global default
|
||||||
MaxLeaseTTL time.Duration `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` // Override for global default
|
MaxLeaseTTL time.Duration `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` // Override for global default
|
||||||
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"` // Override for global default
|
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"` // Override for global default
|
||||||
|
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIMountConfig is an embedded struct of api.MountConfigInput
|
||||||
|
type APIMountConfig struct {
|
||||||
|
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||||
|
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||||
|
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
||||||
|
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mount is used to mount a new backend to the mount table.
|
// Mount is used to mount a new backend to the mount table.
|
||||||
@@ -216,8 +225,13 @@ func (c *Core) mount(entry *MountEntry) error {
|
|||||||
viewPath := backendBarrierPrefix + entry.UUID + "/"
|
viewPath := backendBarrierPrefix + entry.UUID + "/"
|
||||||
view := NewBarrierView(c.barrier, viewPath)
|
view := NewBarrierView(c.barrier, viewPath)
|
||||||
sysView := c.mountEntrySysView(entry)
|
sysView := c.mountEntrySysView(entry)
|
||||||
|
conf := make(map[string]string)
|
||||||
|
if entry.Config.PluginName != "" {
|
||||||
|
conf["plugin_name"] = entry.Config.PluginName
|
||||||
|
}
|
||||||
|
|
||||||
backend, err := c.newLogicalBackend(entry.Type, sysView, view, nil)
|
// Consider having plugin name under entry.Options
|
||||||
|
backend, err := c.newLogicalBackend(entry.Type, sysView, view, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -225,8 +239,14 @@ func (c *Core) mount(entry *MountEntry) error {
|
|||||||
return fmt.Errorf("nil backend of type %q returned from creation function", entry.Type)
|
return fmt.Errorf("nil backend of type %q returned from creation function", entry.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for the correct backend type
|
||||||
|
backendType := backend.Type()
|
||||||
|
if entry.Type == "plugin" && backendType != logical.TypeLogical {
|
||||||
|
return fmt.Errorf("cannot mount '%s' of type '%s' as a logical backend", entry.Config.PluginName, backendType)
|
||||||
|
}
|
||||||
|
|
||||||
// Call initialize; this takes care of init tasks that must be run after
|
// Call initialize; this takes care of init tasks that must be run after
|
||||||
// the ignore paths are collected
|
// the ignore paths are collected.
|
||||||
if err := backend.Initialize(); err != nil {
|
if err := backend.Initialize(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -658,9 +678,13 @@ func (c *Core) setupMounts() error {
|
|||||||
// Create a barrier view using the UUID
|
// Create a barrier view using the UUID
|
||||||
view = NewBarrierView(c.barrier, barrierPath)
|
view = NewBarrierView(c.barrier, barrierPath)
|
||||||
sysView := c.mountEntrySysView(entry)
|
sysView := c.mountEntrySysView(entry)
|
||||||
// Initialize the backend
|
// Set up conf to pass in plugin_name
|
||||||
|
conf := make(map[string]string)
|
||||||
|
if entry.Config.PluginName != "" {
|
||||||
|
conf["plugin_name"] = entry.Config.PluginName
|
||||||
|
}
|
||||||
// Create the new backend
|
// Create the new backend
|
||||||
backend, err = c.newLogicalBackend(entry.Type, sysView, view, nil)
|
backend, err = c.newLogicalBackend(entry.Type, sysView, view, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Error("core: failed to create mount entry", "path", entry.Path, "error", err)
|
c.logger.Error("core: failed to create mount entry", "path", entry.Path, "error", err)
|
||||||
return errLoadMountsFailed
|
return errLoadMountsFailed
|
||||||
@@ -669,6 +693,12 @@ func (c *Core) setupMounts() error {
|
|||||||
return fmt.Errorf("created mount entry of type %q is nil", entry.Type)
|
return fmt.Errorf("created mount entry of type %q is nil", entry.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for the correct backend type
|
||||||
|
backendType := backend.Type()
|
||||||
|
if entry.Type == "plugin" && backendType != logical.TypeLogical {
|
||||||
|
return fmt.Errorf("cannot mount '%s' of type '%s' as a logical backend", entry.Config.PluginName, backendType)
|
||||||
|
}
|
||||||
|
|
||||||
if err := backend.Initialize(); err != nil {
|
if err := backend.Initialize(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -687,10 +717,9 @@ func (c *Core) setupMounts() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Error("core: failed to mount entry", "path", entry.Path, "error", err)
|
c.logger.Error("core: failed to mount entry", "path", entry.Path, "error", err)
|
||||||
return errLoadMountsFailed
|
return errLoadMountsFailed
|
||||||
} else {
|
}
|
||||||
if c.logger.IsInfo() {
|
if c.logger.IsInfo() {
|
||||||
c.logger.Info("core: successfully mounted backend", "type", entry.Type, "path", entry.Path)
|
c.logger.Info("core: successfully mounted backend", "type", entry.Type, "path", entry.Path)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the path is tainted if set in the mount table
|
// Ensure the path is tainted if set in the mount table
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package vault
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -9,7 +10,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/go-uuid"
|
"github.com/hashicorp/go-uuid"
|
||||||
|
"github.com/hashicorp/vault/helper/logformat"
|
||||||
"github.com/hashicorp/vault/logical"
|
"github.com/hashicorp/vault/logical"
|
||||||
|
log "github.com/mgutz/logxi/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NoopBackend struct {
|
type NoopBackend struct {
|
||||||
@@ -63,10 +66,26 @@ func (n *NoopBackend) InvalidateKey(k string) {
|
|||||||
n.Invalidations = append(n.Invalidations, k)
|
n.Invalidations = append(n.Invalidations, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *NoopBackend) Setup(config *logical.BackendConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NoopBackend) Logger() log.Logger {
|
||||||
|
return logformat.NewVaultLoggerWithWriter(ioutil.Discard, log.LevelOff)
|
||||||
|
}
|
||||||
|
|
||||||
func (n *NoopBackend) Initialize() error {
|
func (n *NoopBackend) Initialize() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *NoopBackend) Type() logical.BackendType {
|
||||||
|
return logical.TypeLogical
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NoopBackend) RegisterLicense(license interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestRouter_Mount(t *testing.T) {
|
func TestRouter_Mount(t *testing.T) {
|
||||||
r := NewRouter()
|
r := NewRouter()
|
||||||
_, barrier, _ := mockBarrier(t)
|
_, barrier, _ := mockBarrier(t)
|
||||||
|
|||||||
@@ -520,6 +520,10 @@ func (n *rawHTTP) System() logical.SystemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *rawHTTP) Logger() log.Logger {
|
||||||
|
return logformat.NewVaultLogger(log.LevelTrace)
|
||||||
|
}
|
||||||
|
|
||||||
func (n *rawHTTP) Cleanup() {
|
func (n *rawHTTP) Cleanup() {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
@@ -533,6 +537,19 @@ func (n *rawHTTP) InvalidateKey(string) {
|
|||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *rawHTTP) Setup(config *logical.BackendConfig) error {
|
||||||
|
// noop
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *rawHTTP) Type() logical.BackendType {
|
||||||
|
return logical.TypeUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *rawHTTP) RegisterLicense(license interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func GenerateRandBytes(length int) ([]byte, error) {
|
func GenerateRandBytes(length int) ([]byte, error) {
|
||||||
if length < 0 {
|
if length < 0 {
|
||||||
return nil, fmt.Errorf("length must be >= 0")
|
return nil, fmt.Errorf("length must be >= 0")
|
||||||
|
|||||||
44
website/source/docs/plugin/index.html.md
Normal file
44
website/source/docs/plugin/index.html.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
layout: "docs"
|
||||||
|
page_title: "Plugin Backends"
|
||||||
|
sidebar_current: "docs-plugin"
|
||||||
|
description: |-
|
||||||
|
Plugin backends are mountable backends that are implemented unsing Vault's plugin system.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Plugin Backends
|
||||||
|
|
||||||
|
Plugin backends are the components in Vault that can be implemented separately from Vault's
|
||||||
|
builtin backends. These backends can be either authentication or secret backends.
|
||||||
|
|
||||||
|
Detailed information regarding the plugin system can be found in the
|
||||||
|
[internals documentation](https://www.vaultproject.io/docs/internals/plugins.html).
|
||||||
|
|
||||||
|
# Mounting/unmounting Plugin Backends
|
||||||
|
|
||||||
|
Before a plugin backend can be mounted, it needs to be registered via the
|
||||||
|
[plugin catalog](https://www.vaultproject.io/docs/internals/plugins.html#plugin-catalog). After
|
||||||
|
the plugin is registered, it can be mounted by specifying the registered plugin name:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ vault mount -path=my-secrets -plugin-name=passthrough-plugin plugin
|
||||||
|
Successfully mounted plugin 'passthrough-plugin' at 'my-secrets'!
|
||||||
|
```
|
||||||
|
|
||||||
|
Listing mounts will display backends that are mounted as plugins, along with the
|
||||||
|
name of plugin backend that is mounted:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ vault mounts
|
||||||
|
Path Type Accessor Plugin Default TTL Max TTL Force No Cache Replication Behavior Description
|
||||||
|
my-secrets/ plugin plugin_deb84140 passthrough-plugin system system false replicated
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Unmounting a plugin backend is the identical to unmounting internal backends:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ vault unmount my-secrets
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -316,6 +316,10 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-plugin") %>>
|
||||||
|
<a href="/docs/plugin/index.html">Plugin Backends</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-vault-enterprise") %>>
|
<li<%= sidebar_current("docs-vault-enterprise") %>>
|
||||||
|
|||||||
Reference in New Issue
Block a user