Vault 3992 ToB Config and Plugins Permissions (#14817)
* updating changes from ent PR * adding changelog * fixing err * fixing semgrep error
This commit is contained in:
parent
9f03f86077
commit
796003ddda
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
core : check uid and permissions of config dir, config file, plugin dir and plugin binaries
|
||||||
|
```
|
|
@ -2518,6 +2518,8 @@ func createCoreConfig(c *ServerCommand, config *server.Config, backend physical.
|
||||||
ClusterName: config.ClusterName,
|
ClusterName: config.ClusterName,
|
||||||
CacheSize: config.CacheSize,
|
CacheSize: config.CacheSize,
|
||||||
PluginDirectory: config.PluginDirectory,
|
PluginDirectory: config.PluginDirectory,
|
||||||
|
PluginFileUid: config.PluginFileUid,
|
||||||
|
PluginFilePermissions: config.PluginFilePermissions,
|
||||||
EnableUI: config.EnableUI,
|
EnableUI: config.EnableUI,
|
||||||
EnableRaw: config.EnableRawEndpoint,
|
EnableRaw: config.EnableRawEndpoint,
|
||||||
DisableSealWrap: config.DisableSealWrap,
|
DisableSealWrap: config.DisableSealWrap,
|
||||||
|
@ -2535,6 +2537,7 @@ func createCoreConfig(c *ServerCommand, config *server.Config, backend physical.
|
||||||
LicensePath: config.LicensePath,
|
LicensePath: config.LicensePath,
|
||||||
DisableSSCTokens: config.DisableSSCTokens,
|
DisableSSCTokens: config.DisableSSCTokens,
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.flagDev {
|
if c.flagDev {
|
||||||
coreConfig.EnableRaw = true
|
coreConfig.EnableRaw = true
|
||||||
coreConfig.DevToken = c.flagDevRootTokenID
|
coreConfig.DevToken = c.flagDevRootTokenID
|
||||||
|
|
|
@ -16,7 +16,9 @@ import (
|
||||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
||||||
"github.com/hashicorp/hcl"
|
"github.com/hashicorp/hcl"
|
||||||
"github.com/hashicorp/hcl/hcl/ast"
|
"github.com/hashicorp/hcl/hcl/ast"
|
||||||
|
"github.com/hashicorp/vault/helper/osutil"
|
||||||
"github.com/hashicorp/vault/internalshared/configutil"
|
"github.com/hashicorp/vault/internalshared/configutil"
|
||||||
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
)
|
)
|
||||||
|
|
||||||
var entConfigValidate = func(_ *Config, _ string) []configutil.ConfigError {
|
var entConfigValidate = func(_ *Config, _ string) []configutil.ConfigError {
|
||||||
|
@ -54,6 +56,11 @@ type Config struct {
|
||||||
|
|
||||||
PluginDirectory string `hcl:"plugin_directory"`
|
PluginDirectory string `hcl:"plugin_directory"`
|
||||||
|
|
||||||
|
PluginFileUid int `hcl:"plugin_file_uid"`
|
||||||
|
|
||||||
|
PluginFilePermissions int `hcl:"-"`
|
||||||
|
PluginFilePermissionsRaw interface{} `hcl:"plugin_file_permissions,alias:PluginFilePermissions"`
|
||||||
|
|
||||||
EnableRawEndpoint bool `hcl:"-"`
|
EnableRawEndpoint bool `hcl:"-"`
|
||||||
EnableRawEndpointRaw interface{} `hcl:"raw_storage_endpoint,alias:EnableRawEndpoint"`
|
EnableRawEndpointRaw interface{} `hcl:"raw_storage_endpoint,alias:EnableRawEndpoint"`
|
||||||
|
|
||||||
|
@ -127,7 +134,6 @@ telemetry {
|
||||||
prometheus_retention_time = "24h"
|
prometheus_retention_time = "24h"
|
||||||
disable_hostname = true
|
disable_hostname = true
|
||||||
}
|
}
|
||||||
|
|
||||||
enable_raw_endpoint = true
|
enable_raw_endpoint = true
|
||||||
|
|
||||||
storage "%s" {
|
storage "%s" {
|
||||||
|
@ -276,6 +282,17 @@ func (c *Config) Merge(c2 *Config) *Config {
|
||||||
result.PluginDirectory = c2.PluginDirectory
|
result.PluginDirectory = c2.PluginDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.PluginFileUid = c.PluginFileUid
|
||||||
|
if c2.PluginFileUid != 0 {
|
||||||
|
result.PluginFileUid = c2.PluginFileUid
|
||||||
|
}
|
||||||
|
|
||||||
|
result.PluginFilePermissions = c.PluginFilePermissions
|
||||||
|
if c2.PluginFilePermissionsRaw != nil {
|
||||||
|
result.PluginFilePermissions = c2.PluginFilePermissions
|
||||||
|
result.PluginFilePermissionsRaw = c2.PluginFilePermissionsRaw
|
||||||
|
}
|
||||||
|
|
||||||
result.DisablePerformanceStandby = c.DisablePerformanceStandby
|
result.DisablePerformanceStandby = c.DisablePerformanceStandby
|
||||||
if c2.DisablePerformanceStandby {
|
if c2.DisablePerformanceStandby {
|
||||||
result.DisablePerformanceStandby = c2.DisablePerformanceStandby
|
result.DisablePerformanceStandby = c2.DisablePerformanceStandby
|
||||||
|
@ -350,6 +367,13 @@ func LoadConfig(path string) (*Config, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if fi.IsDir() {
|
if fi.IsDir() {
|
||||||
|
// check permissions on the config directory
|
||||||
|
if os.Getenv(consts.VaultDisableFilePermissionsCheckEnv) != "true" {
|
||||||
|
err = osutil.OwnerPermissionsMatch(path, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
return CheckConfig(LoadConfigDir(path))
|
return CheckConfig(LoadConfigDir(path))
|
||||||
}
|
}
|
||||||
return CheckConfig(LoadConfigFile(path))
|
return CheckConfig(LoadConfigFile(path))
|
||||||
|
@ -385,6 +409,21 @@ func LoadConfigFile(path string) (*Config, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if os.Getenv(consts.VaultDisableFilePermissionsCheckEnv) != "true" {
|
||||||
|
// check permissions of the config file
|
||||||
|
err = osutil.OwnerPermissionsMatch(path, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// check permissions of the plugin directory
|
||||||
|
if conf.PluginDirectory != "" {
|
||||||
|
|
||||||
|
err = osutil.OwnerPermissionsMatch(conf.PluginDirectory, conf.PluginFileUid, conf.PluginFilePermissions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return conf, nil
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,6 +498,18 @@ func ParseConfig(d, source string) (*Config, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if result.PluginFilePermissionsRaw != nil {
|
||||||
|
octalPermissionsString, err := parseutil.ParseString(result.PluginFilePermissionsRaw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pluginFilePermissions, err := strconv.ParseInt(octalPermissionsString, 8, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.PluginFilePermissions = int(pluginFilePermissions)
|
||||||
|
}
|
||||||
|
|
||||||
if result.DisableSentinelTraceRaw != nil {
|
if result.DisableSentinelTraceRaw != nil {
|
||||||
if result.DisableSentinelTrace, err = parseutil.ParseBool(result.DisableSentinelTraceRaw); err != nil {
|
if result.DisableSentinelTrace, err = parseutil.ParseBool(result.DisableSentinelTraceRaw); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -838,6 +889,10 @@ func (c *Config) Sanitized() map[string]interface{} {
|
||||||
|
|
||||||
"plugin_directory": c.PluginDirectory,
|
"plugin_directory": c.PluginDirectory,
|
||||||
|
|
||||||
|
"plugin_file_uid": c.PluginFileUid,
|
||||||
|
|
||||||
|
"plugin_file_permissions": c.PluginFilePermissions,
|
||||||
|
|
||||||
"raw_storage_endpoint": c.EnableRawEndpoint,
|
"raw_storage_endpoint": c.EnableRawEndpoint,
|
||||||
|
|
||||||
"api_addr": c.APIAddr,
|
"api_addr": c.APIAddr,
|
||||||
|
|
|
@ -694,6 +694,8 @@ func testConfig_Sanitized(t *testing.T) {
|
||||||
"disable_indexing": false,
|
"disable_indexing": false,
|
||||||
"disable_mlock": true,
|
"disable_mlock": true,
|
||||||
"disable_performance_standby": false,
|
"disable_performance_standby": false,
|
||||||
|
"plugin_file_uid": 0,
|
||||||
|
"plugin_file_permissions": 0,
|
||||||
"disable_printable_check": false,
|
"disable_printable_check": false,
|
||||||
"disable_sealwrap": true,
|
"disable_sealwrap": true,
|
||||||
"raw_storage_endpoint": true,
|
"raw_storage_endpoint": true,
|
||||||
|
@ -855,6 +857,7 @@ func testParseSockaddrTemplate(t *testing.T) {
|
||||||
api_addr = <<EOF
|
api_addr = <<EOF
|
||||||
{{- GetAllInterfaces | include "flags" "loopback" | include "type" "ipv4" | attr "address" -}}
|
{{- GetAllInterfaces | include "flags" "loopback" | include "type" "ipv4" | attr "address" -}}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
listener "tcp" {
|
listener "tcp" {
|
||||||
address = <<EOF
|
address = <<EOF
|
||||||
{{- GetAllInterfaces | include "flags" "loopback" | include "type" "ipv4" | attr "address" -}}:443
|
{{- GetAllInterfaces | include "flags" "loopback" | include "type" "ipv4" | attr "address" -}}:443
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package osutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsWriteGroup(mode os.FileMode) bool {
|
||||||
|
return mode&0o20 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsWriteOther(mode os.FileMode) bool {
|
||||||
|
return mode&0o02 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPathInfo(info fs.FileInfo, path string, uid int, permissions int) error {
|
||||||
|
err := FileUidMatch(info, path, uid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = FilePermissionsMatch(info, path, permissions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilePermissionsMatch(info fs.FileInfo, path string, permissions int) error {
|
||||||
|
if permissions != 0 && int(info.Mode().Perm()) != permissions {
|
||||||
|
return fmt.Errorf("path %q does not have permissions %o", path, permissions)
|
||||||
|
}
|
||||||
|
if permissions == 0 && (IsWriteOther(info.Mode()) || IsWriteGroup(info.Mode())) {
|
||||||
|
return fmt.Errorf("path %q has insecure permissions %o. Vault expects no write permissions for group or others", path, info.Mode().Perm())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnerPermissionsMatch checks if vault user is the owner and permissions are secure for input path
|
||||||
|
func OwnerPermissionsMatch(path string, uid int, permissions int) error {
|
||||||
|
if path == "" {
|
||||||
|
return fmt.Errorf("could not verify permissions for path. No path provided ")
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error stating %q: %w", path, err)
|
||||||
|
}
|
||||||
|
if info.Mode()&os.ModeSymlink != 0 {
|
||||||
|
symLinkInfo, err := os.Lstat(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error stating %q: %w", path, err)
|
||||||
|
}
|
||||||
|
err = checkPathInfo(symLinkInfo, path, uid, permissions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = checkPathInfo(info, path, uid, permissions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package osutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckPathInfo(t *testing.T) {
|
||||||
|
currentUser, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to get details of current process owner. The error is: %v", err)
|
||||||
|
}
|
||||||
|
uid, err := strconv.ParseInt(currentUser.Uid, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to convert uid to int64. The error is: %v", err)
|
||||||
|
}
|
||||||
|
uid2, err := strconv.ParseInt(currentUser.Uid+"1", 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to convert uid to int64. The error is: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
uid int
|
||||||
|
filepermissions fs.FileMode
|
||||||
|
permissions int
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
uid: 0,
|
||||||
|
filepermissions: 0o700,
|
||||||
|
permissions: 0,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: int(uid2),
|
||||||
|
filepermissions: 0o700,
|
||||||
|
permissions: 0,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: int(uid),
|
||||||
|
filepermissions: 0o700,
|
||||||
|
permissions: 0,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: 0,
|
||||||
|
filepermissions: 0o777,
|
||||||
|
permissions: 744,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
err := os.Mkdir("testFile", tc.filepermissions)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
info, err := os.Stat("testFile")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error stating %q: %v", "testFile", err)
|
||||||
|
}
|
||||||
|
if tc.uid != 0 && runtime.GOOS == "windows" && tc.expectError == true {
|
||||||
|
t.Skip("Skipping test in windows environment as no error will be returned in this case")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = checkPathInfo(info, "testFile", tc.uid, int(tc.permissions))
|
||||||
|
if tc.expectError && err == nil {
|
||||||
|
t.Errorf("invalid result. expected error")
|
||||||
|
}
|
||||||
|
if !tc.expectError && err != nil {
|
||||||
|
t.Errorf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.RemoveAll("testFile")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package osutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"os/user"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FileUIDEqual(info fs.FileInfo, uid int) bool {
|
||||||
|
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
|
||||||
|
path_uid := int(stat.Uid)
|
||||||
|
if path_uid == uid {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func FileGIDEqual(info fs.FileInfo, gid int) bool {
|
||||||
|
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
|
||||||
|
path_gid := int(stat.Gid)
|
||||||
|
if path_gid == gid {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func FileUidMatch(info fs.FileInfo, path string, uid int) (err error) {
|
||||||
|
currentUser, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get details of current process owner. The error is: %w", err)
|
||||||
|
}
|
||||||
|
switch uid {
|
||||||
|
case 0:
|
||||||
|
currentUserUid, err := strconv.Atoi(currentUser.Uid)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to convert uid %q to int. The error is: %w", currentUser.Uid, err)
|
||||||
|
}
|
||||||
|
if !FileUIDEqual(info, currentUserUid) {
|
||||||
|
return fmt.Errorf("path %q is not owned by my uid %s", path, currentUser.Uid)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if !FileUIDEqual(info, uid) {
|
||||||
|
return fmt.Errorf("path %q is not owned by uid %d", path, uid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package osutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFileUIDEqual(t *testing.T) {
|
||||||
|
currentUser, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to get details of current process owner. The error is: %v", err)
|
||||||
|
}
|
||||||
|
uid, err := strconv.Atoi(currentUser.Uid)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to convert uid to int. The error is: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
uid int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
uid: uid,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: uid + 1,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
err := os.Mkdir("testFile", 0o777)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
info, err := os.Stat("testFile")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error stating %q: %v", "testFile", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := FileUIDEqual(info, tc.uid)
|
||||||
|
if result != tc.expected {
|
||||||
|
t.Errorf("invalid result. expected %t for uid %v", tc.expected, tc.uid)
|
||||||
|
}
|
||||||
|
err = os.RemoveAll("testFile")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileGIDEqual(t *testing.T) {
|
||||||
|
currentUser, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to get details of current process owner. The error is: %v", err)
|
||||||
|
}
|
||||||
|
gid, err := strconv.Atoi(currentUser.Gid)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to convert gid to int. The error is: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
gid int
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
gid: gid,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gid: gid + 1,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
err := os.Mkdir("testFile", 0o777)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
info, err := os.Stat("testFile")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error stating %q: %v", "testFile", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := FileGIDEqual(info, tc.gid)
|
||||||
|
if result != tc.expected {
|
||||||
|
t.Errorf("invalid result. expected %t for gid %v", tc.expected, tc.gid)
|
||||||
|
}
|
||||||
|
err = os.RemoveAll("testFile")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package osutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FileUidMatch(info fs.FileInfo, path string, uid int) error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ func getPluginClusterAndCore(t testing.TB, logger log.Logger) (*vault.TestCluste
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
os.Setenv(consts.VaultDisableFilePermissionsCheckEnv, "true")
|
||||||
|
|
||||||
coreConfig := &vault.CoreConfig{
|
coreConfig := &vault.CoreConfig{
|
||||||
Physical: inm,
|
Physical: inm,
|
||||||
|
|
|
@ -46,6 +46,8 @@ func TestSysConfigState_Sanitized(t *testing.T) {
|
||||||
"max_lease_ttl": json.Number("0"),
|
"max_lease_ttl": json.Number("0"),
|
||||||
"pid_file": "",
|
"pid_file": "",
|
||||||
"plugin_directory": "",
|
"plugin_directory": "",
|
||||||
|
"plugin_file_uid": json.Number("0"),
|
||||||
|
"plugin_file_permissions": json.Number("0"),
|
||||||
"enable_response_header_hostname": false,
|
"enable_response_header_hostname": false,
|
||||||
"enable_response_header_raft_node_id": false,
|
"enable_response_header_raft_node_id": false,
|
||||||
"log_requests_level": "",
|
"log_requests_level": "",
|
||||||
|
|
|
@ -32,4 +32,6 @@ const (
|
||||||
// ReplicationResolverALPN is the negotiated protocol used for
|
// ReplicationResolverALPN is the negotiated protocol used for
|
||||||
// resolving replicaiton addresses
|
// resolving replicaiton addresses
|
||||||
ReplicationResolverALPN = "replication_resolver_v1"
|
ReplicationResolverALPN = "replication_resolver_v1"
|
||||||
|
|
||||||
|
VaultDisableFilePermissionsCheckEnv = "VAULT_DISABLE_FILE_PERMISSIONS_CHECK"
|
||||||
)
|
)
|
||||||
|
|
|
@ -915,7 +915,7 @@ func (c *Core) newCredentialBackend(ctx context.Context, entry *MountEntry, sysV
|
||||||
|
|
||||||
f, ok := c.credentialBackends[t]
|
f, ok := c.credentialBackends[t]
|
||||||
if !ok {
|
if !ok {
|
||||||
f = plugin.Factory
|
f = wrapFactoryCheckPerms(c, plugin.Factory)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up conf to pass in plugin_name
|
// Set up conf to pass in plugin_name
|
||||||
|
|
|
@ -37,6 +37,7 @@ import (
|
||||||
"github.com/hashicorp/vault/command/server"
|
"github.com/hashicorp/vault/command/server"
|
||||||
"github.com/hashicorp/vault/helper/metricsutil"
|
"github.com/hashicorp/vault/helper/metricsutil"
|
||||||
"github.com/hashicorp/vault/helper/namespace"
|
"github.com/hashicorp/vault/helper/namespace"
|
||||||
|
"github.com/hashicorp/vault/helper/osutil"
|
||||||
"github.com/hashicorp/vault/physical/raft"
|
"github.com/hashicorp/vault/physical/raft"
|
||||||
"github.com/hashicorp/vault/sdk/helper/certutil"
|
"github.com/hashicorp/vault/sdk/helper/certutil"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
|
@ -487,6 +488,12 @@ type Core struct {
|
||||||
// pluginDirectory is the location vault will look for plugin binaries
|
// pluginDirectory is the location vault will look for plugin binaries
|
||||||
pluginDirectory string
|
pluginDirectory string
|
||||||
|
|
||||||
|
// pluginFileUid is the uid of the plugin files and directory
|
||||||
|
pluginFileUid int
|
||||||
|
|
||||||
|
// pluginFilePermissions is the permissions of the plugin files and directory
|
||||||
|
pluginFilePermissions int
|
||||||
|
|
||||||
// pluginCatalog is used to manage plugin configurations
|
// pluginCatalog is used to manage plugin configurations
|
||||||
pluginCatalog *PluginCatalog
|
pluginCatalog *PluginCatalog
|
||||||
|
|
||||||
|
@ -684,6 +691,10 @@ type CoreConfig struct {
|
||||||
|
|
||||||
PluginDirectory string
|
PluginDirectory string
|
||||||
|
|
||||||
|
PluginFileUid int
|
||||||
|
|
||||||
|
PluginFilePermissions int
|
||||||
|
|
||||||
DisableSealWrap bool
|
DisableSealWrap bool
|
||||||
|
|
||||||
RawConfig *server.Config
|
RawConfig *server.Config
|
||||||
|
@ -998,6 +1009,13 @@ func NewCore(conf *CoreConfig) (*Core, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conf.PluginFileUid != 0 {
|
||||||
|
c.pluginFileUid = conf.PluginFileUid
|
||||||
|
}
|
||||||
|
if conf.PluginFilePermissions != 0 {
|
||||||
|
c.pluginFilePermissions = conf.PluginFilePermissions
|
||||||
|
}
|
||||||
|
|
||||||
createSecondaries(c, conf)
|
createSecondaries(c, conf)
|
||||||
|
|
||||||
if conf.HAPhysical != nil && conf.HAPhysical.HAEnabled() {
|
if conf.HAPhysical != nil && conf.HAPhysical.HAEnabled() {
|
||||||
|
@ -3201,3 +3219,18 @@ func (c *Core) GetHAPeerNodesCached() []PeerNode {
|
||||||
}
|
}
|
||||||
return nodes
|
return nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Core) CheckPluginPerms(pluginName string) (err error) {
|
||||||
|
if c.pluginDirectory != "" && os.Getenv(consts.VaultDisableFilePermissionsCheckEnv) != "true" {
|
||||||
|
err = osutil.OwnerPermissionsMatch(c.pluginDirectory, c.pluginFileUid, c.pluginFilePermissions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fullPath := filepath.Join(c.pluginDirectory, pluginName)
|
||||||
|
err = osutil.OwnerPermissionsMatch(fullPath, c.pluginFileUid, c.pluginFilePermissions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -4,17 +4,17 @@ package diagnose
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"syscall"
|
|
||||||
|
"github.com/hashicorp/vault/helper/osutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsOwnedByRoot checks if a file is owned by root
|
// IsOwnedByRoot checks if a file is owned by root
|
||||||
func IsOwnedByRoot(info fs.FileInfo) bool {
|
func IsOwnedByRoot(info fs.FileInfo) bool {
|
||||||
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
|
if !osutil.FileUIDEqual(info, 0) {
|
||||||
uid := int(stat.Uid)
|
|
||||||
gid := int(stat.Gid)
|
|
||||||
if uid == 0 && gid == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if !osutil.FileGIDEqual(info, 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -450,6 +450,10 @@ func (b *SystemBackend) handlePluginCatalogUpdate(ctx context.Context, req *logi
|
||||||
return logical.ErrorResponse("missing command value"), nil
|
return logical.ErrorResponse("missing command value"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = b.Core.CheckPluginPerms(command); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// For backwards compatibility, also accept args as part of command. Don't
|
// For backwards compatibility, also accept args as part of command. Don't
|
||||||
// accepts args in both command and args.
|
// accepts args in both command and args.
|
||||||
args := d.Get("args").([]string)
|
args := d.Get("args").([]string)
|
||||||
|
|
|
@ -530,6 +530,8 @@ func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType lo
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
os.Setenv(consts.VaultDisableFilePermissionsCheckEnv, "true")
|
||||||
|
|
||||||
// Create a tempdir, cluster.Cleanup will clean up this directory
|
// Create a tempdir, cluster.Cleanup will clean up this directory
|
||||||
tempDir, err := ioutil.TempDir("", "vault-test-cluster")
|
tempDir, err := ioutil.TempDir("", "vault-test-cluster")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -602,7 +604,7 @@ func testSystemBackend_SingleCluster_Env(t *testing.T, env []string) *vault.Test
|
||||||
"test": plugin.Factory,
|
"test": plugin.Factory,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
os.Setenv(consts.VaultDisableFilePermissionsCheckEnv, "true")
|
||||||
// Create a tempdir, cluster.Cleanup will clean up this directory
|
// Create a tempdir, cluster.Cleanup will clean up this directory
|
||||||
tempDir, err := ioutil.TempDir("", "vault-test-cluster")
|
tempDir, err := ioutil.TempDir("", "vault-test-cluster")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ func (c *Core) newLogicalBackend(ctx context.Context, entry *MountEntry, sysView
|
||||||
|
|
||||||
f, ok := c.logicalBackends[t]
|
f, ok := c.logicalBackends[t]
|
||||||
if !ok {
|
if !ok {
|
||||||
f = plugin.Factory
|
f = wrapFactoryCheckPerms(c, plugin.Factory)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up conf to pass in plugin_name
|
// Set up conf to pass in plugin_name
|
||||||
|
|
|
@ -81,6 +81,15 @@ type pluginClient struct {
|
||||||
plugin.ClientProtocol
|
plugin.ClientProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wrapFactoryCheckPerms(core *Core, f logical.Factory) logical.Factory {
|
||||||
|
return func(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
|
if err := core.CheckPluginPerms(conf.Config["plugin_name"]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f(ctx, conf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Core) setupPluginCatalog(ctx context.Context) error {
|
func (c *Core) setupPluginCatalog(ctx context.Context) error {
|
||||||
c.pluginCatalog = &PluginCatalog{
|
c.pluginCatalog = &PluginCatalog{
|
||||||
builtinRegistry: c.builtinRegistry,
|
builtinRegistry: c.builtinRegistry,
|
||||||
|
|
Loading…
Reference in New Issue