open-vault/vault/diagnose/raft_checks.go
Hridoy Roy e38f991054
Diagnose checks for raft quorum status and file backend permissions (#11771)
* raft file and quorum checks

* raft checks

* backup

* raft file checks test

* address comments and add more raft and file and process checks

* syntax issues

* modularize functions to compile differently on different os

* compile raft checks everywhere

* more build tag issues

* raft-diagnose

* correct file permission checks

* upgrade tests and add a getConfigOffline test that currently does not work

* comment

* update file checks method signature on windows

* Update physical/raft/raft_test.go

Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com>

* raft tests

* add todo comment for windows root ownership

* voter count message

* raft checks test fixes

Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com>
2021-06-17 10:04:21 -07:00

97 lines
3.1 KiB
Go

package diagnose
import (
"context"
"errors"
"fmt"
"os"
"runtime"
"strings"
"github.com/hashicorp/vault/physical/raft"
)
const DatabaseFilename = "vault.db"
const owner = "owner"
const group = "group"
const other = "other"
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, "raft folder permission checks", fmt.Errorf("error computing file permissions: %w", err))
}
if !IsDir(info) {
SpotError(ctx, "raft folder ownership checks", fmt.Errorf("error: path does not point to folder"))
}
if !HasDB(path) {
SpotWarn(ctx, "raft folder ownership checks", "boltDB file has not been created")
}
hasOnlyOwnerRW, errs := CheckFilePerms(info)
if errs != nil {
for _, err := range errs {
switch {
case strings.Contains(err, FileIsSymlinkWarning) || strings.Contains(err, FileTooPermissiveWarning):
SpotWarn(ctx, "raft folder permission checks", err)
case strings.Contains(err, FilePermissionsMissingWarning):
SpotError(ctx, "raft folder permission checks", errors.New(err))
}
}
}
ownedByRoot := IsOwnedByRoot(info)
requiresRoot := ownedByRoot && hasOnlyOwnerRW
if requiresRoot {
SpotWarn(ctx, "raft folder ownership checks", "raft backend files owned by root and only accessible as root or with overpermissive file perms. 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, "raft folder permission checks", "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, "raft folder permission checks", "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, "raft quorum", 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 := "warning: only one server node found. Vault is not running in high availability mode"
SpotWarn(ctx, "raft quorum", nonHAWarning)
return nonHAWarning
}
var warnMsg string
if voterCount%2 == 0 {
warnMsg = fmt.Sprintf("error: even number of voters found: %d", voterCount)
SpotWarn(ctx, "raft quorum", warnMsg)
return warnMsg
}
if voterCount > 7 {
warnMsg = fmt.Sprintf("very large cluster detected: %d voters", voterCount)
SpotWarn(ctx, "raft quorum", warnMsg)
return warnMsg
}
okMsg := fmt.Sprintf("voter quorum exists: %d voters", voterCount)
SpotOk(ctx, "raft quorum", okMsg)
return okMsg
}