Skip to content

Commit

Permalink
Implement sys/seal-status and sys/leader in system backend (#10725)
Browse files Browse the repository at this point in the history
* Implement sys/seal-status and sys/leader as normal API calls
(so that they can be used in namespaces.)
* Added changelog.
  • Loading branch information
Mark Gritter authored Jan 20, 2021
1 parent 37541e2 commit 1da5b9a
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 144 deletions.
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.
```
47 changes: 4 additions & 43 deletions http/sys_leader.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package http

import (
"net/http"
"time"

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

// 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 @@ -20,48 +19,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 isLeader {
resp.ActiveTime = core.ActiveTime()
}
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"`
ActiveTime time.Time `json:"active_time,omitempty"`
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
164 changes: 164 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 @@ -3670,6 +3672,168 @@ 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"`
ActiveTime time.Time `json:"active_time,omitempty"`
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 isLeader {
resp.ActiveTime = core.ActiveTime()
}
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

0 comments on commit 1da5b9a

Please sign in to comment.