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

Backport 1.6.x: "vault status" should work in any namespace. #10762

Merged
merged 3 commits into from
Jan 25, 2021
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/10725.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note: improvement
core (enterprise): "vault status" command works when a namespace is set.
```
5 changes: 5 additions & 0 deletions command/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ func (c *StatusCommand) Run(args []string) int {
return 1
}

// Always query in the root namespace.
// Although seal-status is present in other namespaces, it will not
// be available until Vault is unsealed.
client.SetNamespace("")

status, err := client.Sys().SealStatus()
if err != nil {
c.UI.Error(fmt.Sprintf("Error checking seal status: %s", err))
Expand Down
39 changes: 3 additions & 36 deletions http/sys_leader.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package http
import (
"net/http"

"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/vault"
)

// This endpoint is needed to answer queries before Vault unseals
// or becomes the leader.
func handleSysLeader(core *vault.Core) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
Expand All @@ -19,44 +20,10 @@ func handleSysLeader(core *vault.Core) http.Handler {
}

func handleSysLeaderGet(core *vault.Core, w http.ResponseWriter, r *http.Request) {
haEnabled := true
isLeader, address, clusterAddr, err := core.Leader()
if errwrap.Contains(err, vault.ErrHANotEnabled.Error()) {
haEnabled = false
err = nil
}
resp, err := core.GetLeaderStatus()
if err != nil {
respondError(w, http.StatusInternalServerError, err)
return
}
resp := &LeaderResponse{
HAEnabled: haEnabled,
IsSelf: isLeader,
LeaderAddress: address,
LeaderClusterAddress: clusterAddr,
PerfStandby: core.PerfStandby(),
}
if resp.PerfStandby {
resp.PerfStandbyLastRemoteWAL = vault.LastRemoteWAL(core)
} else if isLeader || !haEnabled {
resp.LastWAL = vault.LastWAL(core)
}

resp.RaftCommittedIndex, resp.RaftAppliedIndex = core.GetRaftIndexes()

respondOk(w, resp)
}

type LeaderResponse struct {
HAEnabled bool `json:"ha_enabled"`
IsSelf bool `json:"is_self"`
LeaderAddress string `json:"leader_address"`
LeaderClusterAddress string `json:"leader_cluster_address"`
PerfStandby bool `json:"performance_standby"`
PerfStandbyLastRemoteWAL uint64 `json:"performance_standby_last_remote_wal"`
LastWAL uint64 `json:"last_wal,omitempty"`

// Raft Indexes for this node
RaftCommittedIndex uint64 `json:"raft_committed_index,omitempty"`
RaftAppliedIndex uint64 `json:"raft_applied_index,omitempty"`
}
80 changes: 2 additions & 78 deletions http/sys_seal.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ import (
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"net/http"

"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/version"
"github.com/hashicorp/vault/vault"
)

Expand Down Expand Up @@ -164,87 +162,13 @@ func handleSysSealStatus(core *vault.Core) http.Handler {

func handleSysSealStatusRaw(core *vault.Core, w http.ResponseWriter, r *http.Request) {
ctx := context.Background()

sealed := core.Sealed()

initialized, err := core.Initialized(ctx)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
return
}

var sealConfig *vault.SealConfig
if core.SealAccess().RecoveryKeySupported() {
sealConfig, err = core.SealAccess().RecoveryConfig(ctx)
} else {
sealConfig, err = core.SealAccess().BarrierConfig(ctx)
}
status, err := core.GetSealStatus(ctx)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
return
}

if sealConfig == nil {
respondOk(w, &SealStatusResponse{
Type: core.SealAccess().BarrierType(),
Initialized: initialized,
Sealed: true,
RecoverySeal: core.SealAccess().RecoveryKeySupported(),
StorageType: core.StorageType(),
Version: version.GetVersion().VersionNumber(),
})
return
}

// Fetch the local cluster name and identifier
var clusterName, clusterID string
if !sealed {
cluster, err := core.Cluster(ctx)
if err != nil {
respondError(w, http.StatusInternalServerError, err)
return
}
if cluster == nil {
respondError(w, http.StatusInternalServerError, fmt.Errorf("failed to fetch cluster details"))
return
}
clusterName = cluster.Name
clusterID = cluster.ID
}

progress, nonce := core.SecretProgress()

respondOk(w, &SealStatusResponse{
Type: sealConfig.Type,
Initialized: initialized,
Sealed: sealed,
T: sealConfig.SecretThreshold,
N: sealConfig.SecretShares,
Progress: progress,
Nonce: nonce,
Version: version.GetVersion().VersionNumber(),
Migration: core.IsInSealMigrationMode() && !core.IsSealMigrated(),
ClusterName: clusterName,
ClusterID: clusterID,
RecoverySeal: core.SealAccess().RecoveryKeySupported(),
StorageType: core.StorageType(),
})
}

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"`
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"`
respondOk(w, status)
}

// Note: because we didn't provide explicit tagging in the past we can't do it
Expand Down
161 changes: 161 additions & 0 deletions vault/logical_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"github.com/hashicorp/vault/sdk/helper/strutil"
"github.com/hashicorp/vault/sdk/helper/wrapping"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/version"
"github.com/mitchellh/mapstructure"
)

Expand Down Expand Up @@ -150,6 +151,7 @@ func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend {
b.Backend.Paths = append(b.Backend.Paths, b.configPaths()...)
b.Backend.Paths = append(b.Backend.Paths, b.rekeyPaths()...)
b.Backend.Paths = append(b.Backend.Paths, b.sealPaths()...)
b.Backend.Paths = append(b.Backend.Paths, b.statusPaths()...)
b.Backend.Paths = append(b.Backend.Paths, b.pluginsCatalogListPaths()...)
b.Backend.Paths = append(b.Backend.Paths, b.pluginsCatalogCRUDPath())
b.Backend.Paths = append(b.Backend.Paths, b.pluginsReloadPath())
Expand Down Expand Up @@ -3660,6 +3662,165 @@ func (b *SystemBackend) pathInternalOpenAPI(ctx context.Context, req *logical.Re
return resp, nil
}

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"`
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"`
}

func (core *Core) GetSealStatus(ctx context.Context) (*SealStatusResponse, error) {
sealed := core.Sealed()

initialized, err := core.Initialized(ctx)
if err != nil {
return nil, err
}

var sealConfig *SealConfig
if core.SealAccess().RecoveryKeySupported() {
sealConfig, err = core.SealAccess().RecoveryConfig(ctx)
} else {
sealConfig, err = core.SealAccess().BarrierConfig(ctx)
}
if err != nil {
return nil, err
}

if sealConfig == nil {
return &SealStatusResponse{
Type: core.SealAccess().BarrierType(),
Initialized: initialized,
Sealed: true,
RecoverySeal: core.SealAccess().RecoveryKeySupported(),
StorageType: core.StorageType(),
Version: version.GetVersion().VersionNumber(),
}, nil
}

// Fetch the local cluster name and identifier
var clusterName, clusterID string
if !sealed {
cluster, err := core.Cluster(ctx)
if err != nil {
return nil, err
}
if cluster == nil {
return nil, fmt.Errorf("failed to fetch cluster details")
}
clusterName = cluster.Name
clusterID = cluster.ID
}

progress, nonce := core.SecretProgress()

return &SealStatusResponse{
Type: sealConfig.Type,
Initialized: initialized,
Sealed: sealed,
T: sealConfig.SecretThreshold,
N: sealConfig.SecretShares,
Progress: progress,
Nonce: nonce,
Version: version.GetVersion().VersionNumber(),
Migration: core.IsInSealMigrationMode() && !core.IsSealMigrated(),
ClusterName: clusterName,
ClusterID: clusterID,
RecoverySeal: core.SealAccess().RecoveryKeySupported(),
StorageType: core.StorageType(),
}, nil
}

type LeaderResponse struct {
HAEnabled bool `json:"ha_enabled"`
IsSelf bool `json:"is_self"`

LeaderAddress string `json:"leader_address"`
LeaderClusterAddress string `json:"leader_cluster_address"`
PerfStandby bool `json:"performance_standby"`
PerfStandbyLastRemoteWAL uint64 `json:"performance_standby_last_remote_wal"`
LastWAL uint64 `json:"last_wal,omitempty"`

// Raft Indexes for this node
RaftCommittedIndex uint64 `json:"raft_committed_index,omitempty"`
RaftAppliedIndex uint64 `json:"raft_applied_index,omitempty"`
}

func (core *Core) GetLeaderStatus() (*LeaderResponse, error) {
haEnabled := true
isLeader, address, clusterAddr, err := core.Leader()
if errwrap.Contains(err, ErrHANotEnabled.Error()) {
haEnabled = false
err = nil
}
if err != nil {
return nil, err
}

resp := &LeaderResponse{
HAEnabled: haEnabled,
IsSelf: isLeader,
LeaderAddress: address,
LeaderClusterAddress: clusterAddr,
PerfStandby: core.PerfStandby(),
}
if resp.PerfStandby {
resp.PerfStandbyLastRemoteWAL = LastRemoteWAL(core)
} else if isLeader || !haEnabled {
resp.LastWAL = LastWAL(core)
}

resp.RaftCommittedIndex, resp.RaftAppliedIndex = core.GetRaftIndexes()
return resp, nil
}

func (b *SystemBackend) handleSealStatus(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
status, err := b.Core.GetSealStatus(ctx)
if err != nil {
return nil, err
}
buf, err := json.Marshal(status)
if err != nil {
return nil, err
}
httpResp := &logical.Response{
Data: map[string]interface{}{
logical.HTTPStatusCode: 200,
logical.HTTPRawBody: buf,
logical.HTTPContentType: "application/json",
},
}
return httpResp, nil
}

func (b *SystemBackend) handleLeaderStatus(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
status, err := b.Core.GetLeaderStatus()
if err != nil {
return nil, err
}
buf, err := json.Marshal(status)
if err != nil {
return nil, err
}
httpResp := &logical.Response{
Data: map[string]interface{}{
logical.HTTPStatusCode: 200,
logical.HTTPRawBody: buf,
logical.HTTPContentType: "application/json",
},
}
return httpResp, nil
}

func sanitizePath(path string) string {
if !strings.HasSuffix(path, "/") {
path += "/"
Expand Down
Loading