open-vault/vault/diagnose/raft_checks.go

105 lines
3.4 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package diagnose
import (
"context"
"errors"
"fmt"
"os"
"runtime"
"strings"
"github.com/hashicorp/vault/physical/raft"
)
const (
DatabaseFilename = "vault.db"
owner = "owner"
group = "group"
other = "other"
)
const (
ownershipTestName = "Check Raft Folder Ownership"
permissionsTestName = "Check Raft Folder Permissions"
raftQuorumTestName = "Check For Raft Quorum"
)
func RaftFileChecks(ctx context.Context, path string) {
// Note: Stat does not return information about the symlink itself, in the case where we are dealing with one.
info, err := os.Stat(path)
if err != nil {
SpotError(ctx, permissionsTestName, fmt.Errorf("Error computing file permissions: %w.", err))
}
if !IsDir(info) {
SpotError(ctx, ownershipTestName, fmt.Errorf("Error: Raft storage path variable does not point to a folder."))
}
if !HasDB(path) {
SpotWarn(ctx, ownershipTestName, "Raft boltDB file has not been created.")
}
hasOnlyOwnerRW, errs := CheckFilePerms(info)
for _, err := range errs {
switch {
case strings.Contains(err, FileIsSymlinkWarning) || strings.Contains(err, FileTooPermissiveWarning):
SpotWarn(ctx, permissionsTestName, err)
case strings.Contains(err, FilePermissionsMissingWarning):
SpotError(ctx, permissionsTestName, errors.New(err))
}
}
ownedByRoot := IsOwnedByRoot(info)
requiresRoot := ownedByRoot && hasOnlyOwnerRW
if requiresRoot {
SpotWarn(ctx, ownershipTestName, "Raft backend files are owned by root and are only accessible as root or with overpermissive file permissions. This prevents Vault from running as a non-privileged user.")
Advise(ctx, "Please change raft path permissions to allow for non-root access.")
}
if runtime.GOOS == "windows" {
SpotWarn(ctx, permissionsTestName, "Diagnose cannot determine if Vault needs to run as root to open boltDB file. Please check these permissions manually.")
} else if errs == nil && !requiresRoot {
SpotOk(ctx, permissionsTestName, "Raft BoltDB file has correct set of permissions.")
}
}
// RaftStorageQuorum checks that there is an odd number of voters present
// It returns the status message for testing purposes
func RaftStorageQuorum(ctx context.Context, b RaftConfigurableStorageBackend) string {
var conf *raft.RaftConfigurationResponse
var err error
conf, err = b.GetConfigurationOffline()
if err != nil {
SpotError(ctx, raftQuorumTestName, fmt.Errorf("Error retrieving server configuration: %w.", err))
return fmt.Sprintf("Error retrieving server configuration: %s.", err.Error())
}
voterCount := 0
for _, s := range conf.Servers {
if s.Voter {
voterCount++
}
}
if voterCount == 1 {
nonHAWarning := "Only one server node found. Vault is not running in high availability mode."
SpotWarn(ctx, raftQuorumTestName, nonHAWarning)
return nonHAWarning
}
var warnMsg string
if voterCount%2 == 0 {
warnMsg = fmt.Sprintf("%d voters found. Please ensure that Vault has access to an odd number of voter nodes.", voterCount)
SpotWarn(ctx, raftQuorumTestName, warnMsg)
return warnMsg
}
if voterCount > 7 {
warnMsg = fmt.Sprintf("Very large cluster detected: %d voters found. ", voterCount)
SpotWarn(ctx, raftQuorumTestName, warnMsg)
return warnMsg
}
okMsg := fmt.Sprintf("Voter quorum exists: %d voters.", voterCount)
SpotOk(ctx, raftQuorumTestName, okMsg)
return okMsg
}