Skip to content

Commit

Permalink
Add high privilege namespace (#21215) (#21647)
Browse files Browse the repository at this point in the history
  • Loading branch information
biazmoreira authored Jul 7, 2023
1 parent d66520d commit 39752b8
Show file tree
Hide file tree
Showing 15 changed files with 141 additions and 37 deletions.
4 changes: 4 additions & 0 deletions changelog/21215.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
```release-note:change
core/namespace (enterprise): Introduce the concept of high-privilege namespace (administrative namespace),
which will have access to some system backend paths that were previously only accessible in the root namespace.
```
4 changes: 4 additions & 0 deletions command/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1422,6 +1422,9 @@ func (c *ServerCommand) Run(args []string) int {
info["HCP resource ID"] = config.HCPLinkConf.Resource.ID
}

infoKeys = append(infoKeys, "administrative namespace")
info["administrative namespace"] = config.AdministrativeNamespacePath

sort.Strings(infoKeys)
c.UI.Output("==> Vault server configuration:\n")

Expand Down Expand Up @@ -2716,6 +2719,7 @@ func createCoreConfig(c *ServerCommand, config *server.Config, backend physical.
LicensePath: config.LicensePath,
DisableSSCTokens: config.DisableSSCTokens,
Experiments: config.Experiments,
AdministrativeNamespacePath: config.AdministrativeNamespacePath,
}

if c.flagDev {
Expand Down
5 changes: 5 additions & 0 deletions command/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,11 @@ func (c *Config) Merge(c2 *Config) *Config {
}
}

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

result.entConfig = c.entConfig.Merge(c2.entConfig)

result.Experiments = mergeExperiments(c.Experiments, c2.Experiments)
Expand Down
6 changes: 6 additions & 0 deletions command/server/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ func TestParseStorage(t *testing.T) {
testParseStorageTemplate(t)
}

// TestConfigWithAdministrativeNamespace tests that .hcl and .json configurations are correctly parsed when the administrative_namespace_path is present.
func TestConfigWithAdministrativeNamespace(t *testing.T) {
testConfigWithAdministrativeNamespaceHcl(t)
testConfigWithAdministrativeNamespaceJson(t)
}

func TestUnknownFieldValidation(t *testing.T) {
testUnknownFieldValidation(t)
}
Expand Down
23 changes: 23 additions & 0 deletions command/server/config_test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,28 @@ func testUnknownFieldValidationHcl(t *testing.T) {
}
}

// testConfigWithAdministrativeNamespaceJson tests that a config with a valid administrative namespace path is correctly validated and loaded.
func testConfigWithAdministrativeNamespaceJson(t *testing.T) {
config, err := LoadConfigFile("./test-fixtures/config_with_valid_admin_ns.json")
require.NoError(t, err)

configErrors := config.Validate("./test-fixtures/config_with_valid_admin_ns.json")
require.Empty(t, configErrors)

require.NotEmpty(t, config.AdministrativeNamespacePath)
}

// testConfigWithAdministrativeNamespaceHcl tests that a config with a valid administrative namespace path is correctly validated and loaded.
func testConfigWithAdministrativeNamespaceHcl(t *testing.T) {
config, err := LoadConfigFile("./test-fixtures/config_with_valid_admin_ns.hcl")
require.NoError(t, err)

configErrors := config.Validate("./test-fixtures/config_with_valid_admin_ns.hcl")
require.Empty(t, configErrors)

require.NotEmpty(t, config.AdministrativeNamespacePath)
}

func testLoadConfigFile_json(t *testing.T) {
config, err := LoadConfigFile("./test-fixtures/config.hcl.json")
if err != nil {
Expand Down Expand Up @@ -816,6 +838,7 @@ func testConfig_Sanitized(t *testing.T) {
"num_lease_metrics_buckets": 168,
"add_lease_metrics_namespace_labels": false,
},
"administrative_namespace_path": "admin/",
}

addExpectedEntSanitizedConfig(expected, []string{"http"})
Expand Down
1 change: 1 addition & 0 deletions command/server/test-fixtures/config3.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@ pid_file = "./pidfile"
raw_storage_endpoint = true
disable_sealwrap = true
disable_sentinel_trace = true
administrative_namespace_path = "admin/"
19 changes: 19 additions & 0 deletions command/server/test-fixtures/config_with_valid_admin_ns.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

storage "raft" {
path = "/path/to/raft"
node_id = "raft_node_1"
}
listener "tcp" {
address = "127.0.0.1:8200"
tls_cert_file = "/path/to/cert.pem"
tls_key_file = "/path/to/key.key"
}
seal "awskms" {
kms_key_id = "alias/kms-unseal-key"
}
service_registration "consul" {
address = "127.0.0.1:8500"
}
administrative_namespace_path = "admin/"
28 changes: 28 additions & 0 deletions command/server/test-fixtures/config_with_valid_admin_ns.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"listener": {
"tcp": {
"address": "0.0.0.0:8200",
"tls_cert_file": "/path/to/cert.pem",
"tls_key_file": "/path/to/key.key"
}
},
"seal": {
"awskms": {
"kms_key_id": "alias/kms-unseal-key"
}
},
"storage": {
"raft": {
"path": "/path/to/raft",
"node_id": "raft_node_1"
}
},
"cluster_addr": "http://127.0.0.1:8201",
"api_addr": "http://127.0.0.1:8200",
"service_registration": {
"consul": {
"address": "127.0.0.1:8500"
}
},
"administrative_namespace_path": "admin/"
}
3 changes: 2 additions & 1 deletion http/sys_config_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ func TestSysConfigState_Sanitized(t *testing.T) {
"type": "tcp",
},
},
"storage": tc.expectedStorageOutput,
"storage": tc.expectedStorageOutput,
"administrative_namespace_path": "",
}

if tc.expectedHAStorageOutput != nil {
Expand Down
15 changes: 9 additions & 6 deletions internalshared/configutil/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ type SharedConfig struct {
PidFile string `hcl:"pid_file"`

ClusterName string `hcl:"cluster_name"`

AdministrativeNamespacePath string `hcl:"administrative_namespace_path"`
}

func ParseConfig(d string) (*SharedConfig, error) {
Expand Down Expand Up @@ -164,12 +166,13 @@ func (c *SharedConfig) Sanitized() map[string]interface{} {
}

result := map[string]interface{}{
"cluster_name": c.ClusterName,
"default_max_request_duration": c.DefaultMaxRequestDuration,
"disable_mlock": c.DisableMlock,
"log_format": c.LogFormat,
"log_level": c.LogLevel,
"pid_file": c.PidFile,
"default_max_request_duration": c.DefaultMaxRequestDuration,
"disable_mlock": c.DisableMlock,
"log_level": c.LogLevel,
"log_format": c.LogFormat,
"pid_file": c.PidFile,
"cluster_name": c.ClusterName,
"administrative_namespace_path": c.AdministrativeNamespacePath,
}

// Optional log related settings
Expand Down
6 changes: 5 additions & 1 deletion vault/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,10 @@ type CoreConfig struct {
PendingRemovalMountsAllowed bool

ExpirationRevokeRetryBase time.Duration

// AdministrativeNamespacePath is used to configure the administrative namespace, which has access to some sys endpoints that are
// only accessible in the root namespace, currently sys/audit-hash and sys/monitor.
AdministrativeNamespacePath string
}

// GetServiceRegistration returns the config's ServiceRegistration, or nil if it does
Expand Down Expand Up @@ -1199,7 +1203,7 @@ func NewCore(conf *CoreConfig) (*Core, error) {
c.AddLogger(identityLogger)
return NewIdentityStore(ctx, c, config, identityLogger)
}
addExtraLogicalBackends(c, logicalBackends)
addExtraLogicalBackends(c, logicalBackends, conf.AdministrativeNamespacePath)
c.logicalBackends = logicalBackends

credentialBackends := make(map[string]logical.Factory)
Expand Down
2 changes: 1 addition & 1 deletion vault/core_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (c *Core) PersistUndoLogs() error { return nil }
func (c *Core) teardownReplicationResolverHandler() {}
func createSecondaries(*Core, *CoreConfig) {}

func addExtraLogicalBackends(*Core, map[string]logical.Factory) {}
func addExtraLogicalBackends(*Core, map[string]logical.Factory, string) {}

func addExtraCredentialBackends(*Core, map[string]logical.Factory) {}

Expand Down
59 changes: 31 additions & 28 deletions vault/logical_system_paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,42 +580,45 @@ func (b *SystemBackend) statusPaths() []*framework.Path {
}
}

func (b *SystemBackend) auditPaths() []*framework.Path {
return []*framework.Path{
{
Pattern: "audit-hash/(?P<path>.+)",
func (b *SystemBackend) auditHashPath() *framework.Path {
return &framework.Path{
Pattern: "audit-hash/(?P<path>.+)",

Fields: map[string]*framework.FieldSchema{
"path": {
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["audit_path"][0]),
},
Fields: map[string]*framework.FieldSchema{
"path": {
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["audit_path"][0]),
},

"input": {
Type: framework.TypeString,
},
"input": {
Type: framework.TypeString,
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.handleAuditHash,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"hash": {
Type: framework.TypeString,
Required: true,
},
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.handleAuditHash,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"hash": {
Type: framework.TypeString,
Required: true,
},
}},
},
},
}},
},
},

HelpSynopsis: strings.TrimSpace(sysHelp["audit-hash"][0]),
HelpDescription: strings.TrimSpace(sysHelp["audit-hash"][1]),
},

HelpSynopsis: strings.TrimSpace(sysHelp["audit-hash"][0]),
HelpDescription: strings.TrimSpace(sysHelp["audit-hash"][1]),
}
}

func (b *SystemBackend) auditPaths() []*framework.Path {
return []*framework.Path{
b.auditHashPath(),
{
Pattern: "audit$",

Expand Down
1 change: 1 addition & 0 deletions vault/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -1680,6 +1680,7 @@ func (c *Core) newLogicalBackend(ctx context.Context, entry *MountEntry, sysView
config.EventsSender = pluginEventSender
}

ctx = namespace.ContextWithNamespace(ctx, entry.namespace)
ctx = context.WithValue(ctx, "core_number", c.coreNumber)
b, err := f(ctx, config)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions vault/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ func TestCoreWithSealAndUINoCleanup(t testing.T, opts *CoreConfig) *Core {
conf.DetectDeadlocks = opts.DetectDeadlocks
conf.Experiments = []string{experiments.VaultExperimentEventsAlpha1}
conf.CensusAgent = opts.CensusAgent
conf.AdministrativeNamespacePath = opts.AdministrativeNamespacePath

if opts.Logger != nil {
conf.Logger = opts.Logger
Expand Down Expand Up @@ -1532,6 +1533,7 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te
coreConfig.DisableSentinelTrace = base.DisableSentinelTrace
coreConfig.ClusterName = base.ClusterName
coreConfig.DisableAutopilot = base.DisableAutopilot
coreConfig.AdministrativeNamespacePath = base.AdministrativeNamespacePath

if base.BuiltinRegistry != nil {
coreConfig.BuiltinRegistry = base.BuiltinRegistry
Expand Down

0 comments on commit 39752b8

Please sign in to comment.