diff --git a/api/sys_seal.go b/api/sys_seal.go index c772ae0fc..0522f2a42 100644 --- a/api/sys_seal.go +++ b/api/sys_seal.go @@ -93,22 +93,23 @@ func sealStatusRequestWithContext(ctx context.Context, c *Sys, r *Request) (*Sea } type SealStatusResponse struct { - Type string `json:"type"` - Initialized bool `json:"initialized"` - Sealed bool `json:"sealed"` - T int `json:"t"` - N int `json:"n"` - Progress int `json:"progress"` - Nonce string `json:"nonce"` - Version string `json:"version"` - BuildDate string `json:"build_date"` - Migration bool `json:"migration"` - ClusterName string `json:"cluster_name,omitempty"` - ClusterID string `json:"cluster_id,omitempty"` - RecoverySeal bool `json:"recovery_seal"` - StorageType string `json:"storage_type,omitempty"` - HCPLinkStatus string `json:"hcp_link_status,omitempty"` - HCPLinkResourceID string `json:"hcp_link_resource_ID,omitempty"` + Type string `json:"type"` + Initialized bool `json:"initialized"` + Sealed bool `json:"sealed"` + T int `json:"t"` + N int `json:"n"` + Progress int `json:"progress"` + Nonce string `json:"nonce"` + Version string `json:"version"` + BuildDate string `json:"build_date"` + Migration bool `json:"migration"` + ClusterName string `json:"cluster_name,omitempty"` + ClusterID string `json:"cluster_id,omitempty"` + RecoverySeal bool `json:"recovery_seal"` + StorageType string `json:"storage_type,omitempty"` + HCPLinkStatus string `json:"hcp_link_status,omitempty"` + HCPLinkResourceID string `json:"hcp_link_resource_ID,omitempty"` + Warnings []string `json:"warnings,omitempty"` } type UnsealOpts struct { diff --git a/changelog/17855.txt b/changelog/17855.txt new file mode 100644 index 000000000..c73838ae3 --- /dev/null +++ b/changelog/17855.txt @@ -0,0 +1,3 @@ +```release-note:improvement +core: Added warning to /sys/seal-status and vault status command if potentially dangerous behaviour overrides are being used. +``` \ No newline at end of file diff --git a/command/format.go b/command/format.go index 1c08cbcc0..6543880fb 100644 --- a/command/format.go +++ b/command/format.go @@ -402,6 +402,9 @@ func (t TableFormatter) OutputSealStatusStruct(ui cli.Ui, secret *api.Secret, da if status.LastWAL != 0 { out = append(out, fmt.Sprintf("Last WAL | %d", status.LastWAL)) } + if len(status.Warnings) > 0 { + out = append(out, fmt.Sprintf("Warnings | %v", status.Warnings)) + } ui.Output(tableOutput(out, &columnize.Config{ Delim: "|", diff --git a/command/format_test.go b/command/format_test.go index 3d1a3333c..9c950af8f 100644 --- a/command/format_test.go +++ b/command/format_test.go @@ -118,7 +118,8 @@ Cluster ID cluster id HA Enabled true Raft Committed Index 3 Raft Applied Index 4 -Last WAL 2` +Last WAL 2 +Warnings [warning]` if expectedOutputString != output { fmt.Printf("%s\n%+v\n %s\n%+v\n", "output found was: ", output, "versus", expectedOutputString) @@ -174,6 +175,7 @@ func getMockStatusData(emptyFields bool) SealStatusOutput { ClusterID: "cluster id", RecoverySeal: true, StorageType: "storage type", + Warnings: []string{"warning"}, } // must initialize this struct without explicit field names due to embedding diff --git a/http/sys_seal_test.go b/http/sys_seal_test.go index e991f4416..698b625b6 100644 --- a/http/sys_seal_test.go +++ b/http/sys_seal_test.go @@ -60,6 +60,80 @@ func TestSysSealStatus(t *testing.T) { } } +func TestSysSealStatus_Warnings(t *testing.T) { + core := vault.TestCore(t) + vault.TestCoreInit(t, core) + ln, addr := TestServer(t, core) + defer ln.Close() + + // Manually configure DisableSSCTokens to be true + core.GetCoreConfigInternal().DisableSSCTokens = true + + resp, err := http.Get(addr + "/v1/sys/seal-status") + if err != nil { + t.Fatalf("err: %s", err) + } + + var actual map[string]interface{} + expected := map[string]interface{}{ + "sealed": true, + "t": json.Number("3"), + "n": json.Number("3"), + "progress": json.Number("0"), + "nonce": "", + "type": "shamir", + "recovery_seal": false, + "initialized": true, + "migration": false, + "build_date": version.BuildDate, + } + testResponseStatus(t, resp, 200) + testResponseBody(t, resp, &actual) + if actual["version"] == nil { + t.Fatalf("expected version information") + } + expected["version"] = actual["version"] + if actual["cluster_name"] == nil { + delete(expected, "cluster_name") + } else { + expected["cluster_name"] = actual["cluster_name"] + } + if actual["cluster_id"] == nil { + delete(expected, "cluster_id") + } else { + expected["cluster_id"] = actual["cluster_id"] + } + actualWarnings := actual["warnings"] + if actualWarnings == nil { + t.Fatalf("expected warnings about SSCToken disabling") + } + + actualWarningsArray, ok := actualWarnings.([]interface{}) + if !ok { + t.Fatalf("expected warnings about SSCToken disabling were not in the right format") + } + if len(actualWarningsArray) != 1 { + t.Fatalf("too many warnings were given") + } + actualWarning, ok := actualWarningsArray[0].(string) + if !ok { + t.Fatalf("expected warning about SSCToken disabling was not in the right format") + } + + expectedWarning := "Server Side Consistent Tokens are disabled, due to the " + + "VAULT_DISABLE_SERVER_SIDE_CONSISTENT_TOKENS environment variable being set. " + + "It is not recommended to run Vault for an extended period of time with this configuration." + if actualWarning != expectedWarning { + t.Fatalf("actual warning was not as expected. Expected %s, but got %s", expectedWarning, actualWarning) + } + + expected["warnings"] = actual["warnings"] + + if diff := deep.Equal(actual, expected); diff != nil { + t.Fatal(diff) + } +} + func TestSysSealStatus_uninit(t *testing.T) { core := vault.TestCore(t) ln, addr := TestServer(t, core) diff --git a/vault/logical_system.go b/vault/logical_system.go index d67ce5f2f..118762712 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -4529,22 +4529,36 @@ func (b *SystemBackend) pathInternalOpenAPI(ctx context.Context, req *logical.Re } type SealStatusResponse struct { - Type string `json:"type"` - Initialized bool `json:"initialized"` - Sealed bool `json:"sealed"` - T int `json:"t"` - N int `json:"n"` - Progress int `json:"progress"` - Nonce string `json:"nonce"` - Version string `json:"version"` - BuildDate string `json:"build_date"` - Migration bool `json:"migration"` - ClusterName string `json:"cluster_name,omitempty"` - ClusterID string `json:"cluster_id,omitempty"` - RecoverySeal bool `json:"recovery_seal"` - StorageType string `json:"storage_type,omitempty"` - HCPLinkStatus string `json:"hcp_link_status,omitempty"` - HCPLinkResourceID string `json:"hcp_link_resource_ID,omitempty"` + Type string `json:"type"` + Initialized bool `json:"initialized"` + Sealed bool `json:"sealed"` + T int `json:"t"` + N int `json:"n"` + Progress int `json:"progress"` + Nonce string `json:"nonce"` + Version string `json:"version"` + BuildDate string `json:"build_date"` + Migration bool `json:"migration"` + ClusterName string `json:"cluster_name,omitempty"` + ClusterID string `json:"cluster_id,omitempty"` + RecoverySeal bool `json:"recovery_seal"` + StorageType string `json:"storage_type,omitempty"` + HCPLinkStatus string `json:"hcp_link_status,omitempty"` + HCPLinkResourceID string `json:"hcp_link_resource_ID,omitempty"` + Warnings []string `json:"warnings,omitempty"` +} + +// getStatusWarnings exposes potentially dangerous overrides in the status response +// currently, this only warns about VAULT_DISABLE_SERVER_SIDE_CONSISTENT_TOKENS, +// but should be extended to report more warnings where appropriate +func (core *Core) getStatusWarnings() []string { + var warnings []string + if core.GetCoreConfigInternal() != nil && core.GetCoreConfigInternal().DisableSSCTokens { + warnings = append(warnings, "Server Side Consistent Tokens are disabled, due to the "+ + "VAULT_DISABLE_SERVER_SIDE_CONSISTENT_TOKENS environment variable being set. "+ + "It is not recommended to run Vault for an extended period of time with this configuration.") + } + return warnings } func (core *Core) GetSealStatus(ctx context.Context) (*SealStatusResponse, error) { @@ -4617,6 +4631,7 @@ func (core *Core) GetSealStatus(ctx context.Context) (*SealStatusResponse, error ClusterID: clusterID, RecoverySeal: core.SealAccess().RecoveryKeySupported(), StorageType: core.StorageType(), + Warnings: core.getStatusWarnings(), } if resourceIDonHCP != "" {