Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support setting plugin TMPDIR in config as well as env #24978

Merged
merged 2 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/24978.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
core: Added new `plugin_tmpdir` config option for containerized plugins, in addition to the existing `VAULT_PLUGIN_TMPDIR` environment variable.
```
3 changes: 3 additions & 0 deletions command/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ const (
// logged at startup _per node_. This was initially introduced for the events
// system being developed over multiple release cycles.
EnvVaultExperiments = "VAULT_EXPERIMENTS"
// EnvVaultPluginTmpdir sets the folder to use for Unix sockets when setting
// up containerized plugins.
EnvVaultPluginTmpdir = "VAULT_PLUGIN_TMPDIR"

// flagNameAddress is the flag used in the base command to read in the
// address of the Vault server.
Expand Down
5 changes: 5 additions & 0 deletions command/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,10 @@ func (c *ServerCommand) Run(args []string) int {
config.License = envLicense
}

if envPluginTmpdir := os.Getenv(EnvVaultPluginTmpdir); envPluginTmpdir != "" {
config.PluginTmpdir = envPluginTmpdir
}

if err := server.ExperimentsFromEnvAndCLI(config, EnvVaultExperiments, c.flagExperiments); err != nil {
c.UI.Error(err.Error())
return 1
Expand Down Expand Up @@ -3072,6 +3076,7 @@ func createCoreConfig(c *ServerCommand, config *server.Config, backend physical.
ClusterName: config.ClusterName,
CacheSize: config.CacheSize,
PluginDirectory: config.PluginDirectory,
PluginTmpdir: config.PluginTmpdir,
PluginFileUid: config.PluginFileUid,
PluginFilePermissions: config.PluginFilePermissions,
EnableUI: config.EnableUI,
Expand Down
7 changes: 7 additions & 0 deletions command/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type Config struct {
ClusterCipherSuites string `hcl:"cluster_cipher_suites"`

PluginDirectory string `hcl:"plugin_directory"`
PluginTmpdir string `hcl:"plugin_tmpdir"`

PluginFileUid int `hcl:"plugin_file_uid"`

Expand Down Expand Up @@ -363,6 +364,11 @@ func (c *Config) Merge(c2 *Config) *Config {
result.PluginDirectory = c2.PluginDirectory
}

result.PluginTmpdir = c.PluginTmpdir
if c2.PluginTmpdir != "" {
result.PluginTmpdir = c2.PluginTmpdir
}

result.PluginFileUid = c.PluginFileUid
if c2.PluginFileUid != 0 {
result.PluginFileUid = c2.PluginFileUid
Expand Down Expand Up @@ -1114,6 +1120,7 @@ func (c *Config) Sanitized() map[string]interface{} {
"cluster_cipher_suites": c.ClusterCipherSuites,

"plugin_directory": c.PluginDirectory,
"plugin_tmpdir": c.PluginTmpdir,

"plugin_file_uid": c.PluginFileUid,

Expand Down
4 changes: 4 additions & 0 deletions command/server/config_test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,9 @@ func testLoadConfigFile(t *testing.T) {
EnableResponseHeaderRaftNodeIDRaw: true,

LicensePath: "/path/to/license",

PluginDirectory: "/path/to/plugins",
PluginTmpdir: "/tmp/plugins",
}

addExpectedEntConfig(expected, []string{})
Expand Down Expand Up @@ -800,6 +803,7 @@ func testConfig_Sanitized(t *testing.T) {
"max_lease_ttl": (30 * 24 * time.Hour) / time.Second,
"pid_file": "./pidfile",
"plugin_directory": "",
"plugin_tmpdir": "",
"seals": []interface{}{
map[string]interface{}{
"disabled": false,
Expand Down
4 changes: 3 additions & 1 deletion command/server/test-fixtures/config.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,6 @@ disable_sealwrap = true
disable_printable_check = true
enable_response_header_hostname = true
enable_response_header_raft_node_id = true
license_path = "/path/to/license"
license_path = "/path/to/license"
plugin_directory = "/path/to/plugins"
plugin_tmpdir = "/tmp/plugins"
1 change: 1 addition & 0 deletions http/sys_config_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ func TestSysConfigState_Sanitized(t *testing.T) {
"max_lease_ttl": json.Number("0"),
"pid_file": "",
"plugin_directory": "",
"plugin_tmpdir": "",
"plugin_file_uid": json.Number("0"),
"plugin_file_permissions": json.Number("0"),
"enable_response_header_hostname": false,
Expand Down
4 changes: 3 additions & 1 deletion sdk/helper/pluginutil/run_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type runConfig struct {
runtimeConfig *pluginruntimeutil.PluginRuntimeConfig

PluginClientConfig
tmpdir string
}

func (rc runConfig) mlockEnabled() bool {
Expand Down Expand Up @@ -144,7 +145,7 @@ func (rc runConfig) makeConfig(ctx context.Context) (*plugin.ClientConfig, error
clientConfig.RunnerFunc = containerCfg.NewContainerRunner
clientConfig.UnixSocketConfig = &plugin.UnixSocketConfig{
Group: strconv.Itoa(containerCfg.GroupAdd),
TempDir: os.Getenv("VAULT_PLUGIN_TMPDIR"),
TempDir: rc.tmpdir,
}
clientConfig.GRPCBrokerMultiplex = true
}
Expand Down Expand Up @@ -271,6 +272,7 @@ func (r *PluginRunner) RunConfig(ctx context.Context, opts ...RunOpt) (*plugin.C
sha256: r.Sha256,
env: r.Env,
runtimeConfig: r.RuntimeConfig,
tmpdir: r.Tmpdir,
PluginClientConfig: PluginClientConfig{
Name: r.Name,
PluginType: r.Type,
Expand Down
1 change: 1 addition & 0 deletions sdk/helper/pluginutil/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type PluginRunner struct {
Builtin bool `json:"builtin" structs:"builtin"`
BuiltinFactory func() (interface{}, error) `json:"-" structs:"-"`
RuntimeConfig *prutil.PluginRuntimeConfig `json:"-" structs:"-"`
Tmpdir string `json:"-" structs:"-"`
}

// BinaryReference returns either the OCI image reference if it's a container
Expand Down
20 changes: 19 additions & 1 deletion vault/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,9 @@ type Core struct {

// pluginDirectory is the location vault will look for plugin binaries
pluginDirectory string
// pluginTmpdir is the location vault will use for containerized plugin
// temporary files
pluginTmpdir string

// pluginFileUid is the uid of the plugin files and directory
pluginFileUid int
Expand Down Expand Up @@ -816,6 +819,7 @@ type CoreConfig struct {
EnableIntrospection bool

PluginDirectory string
PluginTmpdir string

PluginFileUid int

Expand Down Expand Up @@ -1230,6 +1234,12 @@ func NewCore(conf *CoreConfig) (*Core, error) {
return nil, fmt.Errorf("core setup failed, could not verify plugin directory: %w", err)
}
}
if conf.PluginTmpdir != "" {
c.pluginTmpdir, err = filepath.Abs(conf.PluginTmpdir)
if err != nil {
return nil, fmt.Errorf("core setup failed, could not verify plugin tmpdir: %w", err)
}
}

if conf.PluginFileUid != 0 {
c.pluginFileUid = conf.PluginFileUid
Expand Down Expand Up @@ -2491,7 +2501,15 @@ func (c *Core) setupPluginRuntimeCatalog(ctx context.Context) error {
// this method can be included in the slice of functions returned by the
// buildUnsealSetupFunctionsSlice function.
func (c *Core) setupPluginCatalog(ctx context.Context) error {
pluginCatalog, err := plugincatalog.SetupPluginCatalog(ctx, c.logger, c.builtinRegistry, NewBarrierView(c.barrier, pluginCatalogPath), c.pluginDirectory, c.enableMlock, c.pluginRuntimeCatalog)
pluginCatalog, err := plugincatalog.SetupPluginCatalog(ctx, &plugincatalog.PluginCatalogInput{
Logger: c.logger,
BuiltinRegistry: c.builtinRegistry,
CatalogView: NewBarrierView(c.barrier, pluginCatalogPath),
PluginDirectory: c.pluginDirectory,
Tmpdir: c.pluginTmpdir,
EnableMlock: c.enableMlock,
PluginRuntimeCatalog: c.pluginRuntimeCatalog,
})
if err != nil {
return err
}
Expand Down
36 changes: 31 additions & 5 deletions vault/external_plugin_container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"github.com/hashicorp/vault/sdk/logical"
)

func testClusterWithContainerPlugins(t *testing.T, types []consts.PluginType) (*Core, []pluginhelpers.TestPlugin) {
func testClusterWithContainerPlugins(t *testing.T, types []consts.PluginType) (*TestClusterCore, []pluginhelpers.TestPlugin) {
var plugins []*TestPluginConfig
for _, typ := range types {
plugins = append(plugins, &TestPluginConfig{
Expand All @@ -26,15 +26,28 @@ func testClusterWithContainerPlugins(t *testing.T, types []consts.PluginType) (*
Container: true,
})
}
cluster := NewTestCluster(t, &CoreConfig{}, &TestClusterOptions{
// Use os.MkdirTemp because t.TempDir() exceeds the Unix socket length limit.
// See https://www.man7.org/linux/man-pages/man7/unix.7.html for details.
tmpdir, err := os.MkdirTemp("", "")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err := os.RemoveAll(tmpdir); err != nil {
t.Fatal(err)
}
})
cluster := NewTestCluster(t, &CoreConfig{
PluginTmpdir: tmpdir,
}, &TestClusterOptions{
Plugins: plugins,
})

cluster.Start()
t.Cleanup(cluster.Cleanup)

core := cluster.Cores[0].Core
TestWaitActive(t, core)
core := cluster.Cores[0]
TestWaitActive(t, core.Core)

return core, cluster.Plugins
}
Expand Down Expand Up @@ -81,14 +94,26 @@ func TestExternalPluginInContainer_MountAndUnmount(t *testing.T) {
})
}

func mountAndUnmountContainerPlugin_WithRuntime(t *testing.T, c *Core, plugin pluginhelpers.TestPlugin, ociRuntime string, rootless bool) {
func mountAndUnmountContainerPlugin_WithRuntime(t *testing.T, c *TestClusterCore, plugin pluginhelpers.TestPlugin, ociRuntime string, rootless bool) {
if ociRuntime != "" {
registerPluginRuntime(t, c.systemBackend, ociRuntime, rootless)
}
registerContainerPlugin(t, c.systemBackend, plugin.Name, plugin.Typ.String(), "1.0.0", plugin.ImageSha256, plugin.Image, ociRuntime)

mountPlugin(t, c.systemBackend, plugin.Name, plugin.Typ, "v1.0.0", "")

expectTmpdirEntries := func(expected int) {
t.Helper()
entries, err := os.ReadDir(c.CoreConfig.PluginTmpdir)
if err != nil {
t.Fatal(err)
}
if len(entries) != expected {
t.Fatalf("expected %d in tmpdir, got %v", expected, entries)
}
}
expectTmpdirEntries(1)

routeRequest := func(expectMatch bool) {
pluginPath := "foo/bar"
if plugin.Typ == consts.PluginTypeCredential {
Expand All @@ -106,6 +131,7 @@ func mountAndUnmountContainerPlugin_WithRuntime(t *testing.T, c *Core, plugin pl
routeRequest(true)
unmountPlugin(t, c.systemBackend, plugin.Typ, "foo")
routeRequest(false)
expectTmpdirEntries(0)
}

func registerContainerPlugin(t *testing.T, sys *SystemBackend, pluginName, pluginType, version, sha, image, runtime string) {
Expand Down
60 changes: 39 additions & 21 deletions vault/plugincatalog/plugin_catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type PluginCatalog struct {
builtinRegistry BuiltinRegistry
catalogView logical.Storage
directory string
tmpdir string
logger log.Logger

// externalPlugins holds plugin process connections by a key which is
Expand Down Expand Up @@ -137,37 +138,42 @@ type pluginClient struct {
plugin.ClientProtocol
}

func SetupPluginCatalog(
ctx context.Context,
logger log.Logger,
builtinRegistry BuiltinRegistry,
catalogView logical.Storage,
pluginDirectory string,
enableMlock bool,
pluginRuntimeCatalog *PluginRuntimeCatalog,
) (*PluginCatalog, error) {
pluginCatalog := &PluginCatalog{
builtinRegistry: builtinRegistry,
catalogView: catalogView,
directory: pluginDirectory,
type PluginCatalogInput struct {
Logger log.Logger
BuiltinRegistry BuiltinRegistry
CatalogView logical.Storage
PluginDirectory string
Tmpdir string
EnableMlock bool
PluginRuntimeCatalog *PluginRuntimeCatalog
}
Comment on lines +141 to +149
Copy link
Member

Choose a reason for hiding this comment

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

👍


func SetupPluginCatalog(ctx context.Context, in *PluginCatalogInput) (*PluginCatalog, error) {
logger := in.Logger
catalog := &PluginCatalog{
builtinRegistry: in.BuiltinRegistry,
catalogView: in.CatalogView,
directory: in.PluginDirectory,
tmpdir: in.Tmpdir,
logger: logger,
mlockPlugins: enableMlock,
mlockPlugins: in.EnableMlock,
wrapper: logical.StaticSystemView{VersionString: version.GetVersion().Version},
runtimeCatalog: pluginRuntimeCatalog,
runtimeCatalog: in.PluginRuntimeCatalog,
}

// Run upgrade if untyped plugins exist
err := pluginCatalog.UpgradePlugins(ctx, logger)
err := catalog.upgradePlugins(ctx, logger)
if err != nil {
logger.Error("error while upgrading plugin storage", "error", err)
return nil, err
}

if logger.IsInfo() {
logger.Info("successfully setup plugin catalog", "plugin-directory", pluginDirectory)
logger.Info("successfully setup plugin catalog", "plugin-directory", catalog.directory)
if catalog.tmpdir != "" {
logger.Debug("plugin temporary directory configured", "tmpdir", catalog.tmpdir)
}

return pluginCatalog, nil
return catalog, nil
}

type pluginClientConn struct {
Expand Down Expand Up @@ -722,9 +728,9 @@ func (c *PluginCatalog) isDatabasePlugin(ctx context.Context, pluginRunner *plug
return merr.ErrorOrNil()
}

// UpgradePlugins will loop over all the plugins of unknown type and attempt to
// upgradePlugins will loop over all the plugins of unknown type and attempt to
// upgrade them to typed plugins
func (c *PluginCatalog) UpgradePlugins(ctx context.Context, logger log.Logger) error {
func (c *PluginCatalog) upgradePlugins(ctx context.Context, logger log.Logger) error {
c.lock.Lock()
defer c.lock.Unlock()

Expand All @@ -738,6 +744,10 @@ func (c *PluginCatalog) UpgradePlugins(ctx context.Context, logger log.Logger) e
if err != nil {
return err
}
if len(pluginsRaw) == 0 {
return nil
}

plugins := make([]string, 0, len(pluginsRaw))
for _, p := range pluginsRaw {
if !strings.HasSuffix(p, "/") {
Expand Down Expand Up @@ -837,6 +847,7 @@ func (c *PluginCatalog) get(ctx context.Context, name string, pluginType consts.
// If none of the cases are satisfied, we'll search for a builtin plugin below.
switch {
case entry.OCIImage != "":
entry.Tmpdir = c.tmpdir
if entry.Runtime != "" {
entry.RuntimeConfig, err = c.runtimeCatalog.Get(ctx, entry.Runtime, consts.PluginRuntimeTypeContainer)
if err != nil {
Expand Down Expand Up @@ -1072,6 +1083,9 @@ func (c *PluginCatalog) ListPluginsWithRuntime(ctx context.Context, runtime stri
if plugin.Runtime == runtime {
ret = append(ret, plugin.Name)
}
if plugin.OCIImage != "" {
plugin.Tmpdir = c.tmpdir
}
}
return ret, nil
}
Expand Down Expand Up @@ -1127,6 +1141,10 @@ func (c *PluginCatalog) listInternal(ctx context.Context, pluginType consts.Plug
continue
}

if plugin.OCIImage != "" {
plugin.Tmpdir = c.tmpdir
}

result = append(result, pluginutil.VersionedPlugin{
Name: plugin.Name,
Type: plugin.Type.String(),
Expand Down
14 changes: 8 additions & 6 deletions vault/plugincatalog/plugin_catalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@ func testPluginCatalog(t *testing.T) *PluginCatalog {
pluginRuntimeCatalog := testPluginRuntimeCatalog(t)
pluginCatalog, err := SetupPluginCatalog(
context.Background(),
logger,
corehelpers.NewMockBuiltinRegistry(),
logical.NewLogicalStorage(storage),
testDir,
false,
pluginRuntimeCatalog,
&PluginCatalogInput{
Logger: logger,
BuiltinRegistry: corehelpers.NewMockBuiltinRegistry(),
CatalogView: logical.NewLogicalStorage(storage),
PluginDirectory: testDir,
EnableMlock: false,
PluginRuntimeCatalog: pluginRuntimeCatalog,
},
)
if err != nil {
t.Fatal(err)
Expand Down
Loading
Loading