5ee7cc5e6d
* Rename common.go->healthcheck.go Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Push handling of no resources to the health checks This allows us to better run on empty mounts. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Exit when no issuers are found This makes health checks less useful. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add additional test criteria, refactor tests This will allow us to setup more tests. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add more OK statuses when checks are good Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add test cases for all bad results The test for too-many-certs was elided for now due to being too hard to setup in CI. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add test for missing mount Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add expected failure test on empty mount Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add test for only having an issuer in the mount Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * More consistently perform permission checks Also return them to the caller when they're relevant. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add test without token Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Run health check tests in parallel Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Update command/healthcheck/healthcheck.go Co-authored-by: Steven Clark <steven.clark@hashicorp.com> * Update command/healthcheck/healthcheck.go Co-authored-by: Steven Clark <steven.clark@hashicorp.com> Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
145 lines
4.1 KiB
Go
145 lines
4.1 KiB
Go
package healthcheck
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
|
|
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
|
)
|
|
|
|
type TidyLastRun struct {
|
|
Enabled bool
|
|
UnsupportedVersion bool
|
|
|
|
LastRunCritical time.Duration
|
|
LastRunWarning time.Duration
|
|
|
|
TidyStatus *PathFetch
|
|
}
|
|
|
|
func NewTidyLastRunCheck() Check {
|
|
return &TidyLastRun{}
|
|
}
|
|
|
|
func (h *TidyLastRun) Name() string {
|
|
return "tidy_last_run"
|
|
}
|
|
|
|
func (h *TidyLastRun) IsEnabled() bool {
|
|
return h.Enabled
|
|
}
|
|
|
|
func (h *TidyLastRun) DefaultConfig() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"last_run_critical": "7d",
|
|
"last_run_warning": "2d",
|
|
}
|
|
}
|
|
|
|
func (h *TidyLastRun) LoadConfig(config map[string]interface{}) error {
|
|
var err error
|
|
h.LastRunCritical, err = parseutil.ParseDurationSecond(config["last_run_critical"])
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse parameter %v.%v=%v: %w", h.Name(), "last_run_critical", config["last_run_critical"], err)
|
|
}
|
|
|
|
h.LastRunWarning, err = parseutil.ParseDurationSecond(config["last_run_warning"])
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse parameter %v.%v=%v: %w", h.Name(), "last_run_warning", config["last_run_warning"], err)
|
|
}
|
|
|
|
enabled, err := parseutil.ParseBool(config["enabled"])
|
|
if err != nil {
|
|
return fmt.Errorf("error parsing %v.enabled: %w", h.Name(), err)
|
|
}
|
|
h.Enabled = enabled
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *TidyLastRun) FetchResources(e *Executor) error {
|
|
var err error
|
|
|
|
h.TidyStatus, err = e.FetchIfNotFetched(logical.ReadOperation, "/{{mount}}/tidy-status")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to fetch mount's tidy-status value: %v", err)
|
|
}
|
|
|
|
if h.TidyStatus.IsUnsupportedPathError() {
|
|
h.UnsupportedVersion = true
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *TidyLastRun) Evaluate(e *Executor) (results []*Result, err error) {
|
|
if h.UnsupportedVersion {
|
|
// Shouldn't happen; roles have been around forever.
|
|
ret := Result{
|
|
Status: ResultInvalidVersion,
|
|
Endpoint: "/{{mount}}/tidy-status",
|
|
Message: "This health check requires Vault 1.10+ but an earlier version of Vault Server was contacted, preventing this health check from running.",
|
|
}
|
|
return []*Result{&ret}, nil
|
|
}
|
|
|
|
if h.TidyStatus == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
if h.TidyStatus.IsSecretPermissionsError() {
|
|
ret := Result{
|
|
Status: ResultInsufficientPermissions,
|
|
Endpoint: "/{{mount}}/tidy-status",
|
|
Message: "Without this information, this health check is unable tof unction.",
|
|
}
|
|
|
|
if e.Client.Token() == "" {
|
|
ret.Message = "No token available so unable read tidy status endpoint for this mount. " + ret.Message
|
|
} else {
|
|
ret.Message = "This token lacks permission to read the tidy status endpoint for this mount. " + ret.Message
|
|
}
|
|
|
|
results = append(results, &ret)
|
|
}
|
|
|
|
baseMsg := "Tidy hasn't run in the last %v; this can point to problems with the mount's auto-tidy configuration or an external tidy executor; this can impact PKI's and Vault's performance if not run regularly."
|
|
|
|
if h.TidyStatus.Secret != nil && h.TidyStatus.Secret.Data != nil {
|
|
ret := Result{
|
|
Status: ResultOK,
|
|
Endpoint: "/{{mount}}/tidy-status",
|
|
Message: "Tidy has run recently on this mount.",
|
|
}
|
|
|
|
when := h.TidyStatus.Secret.Data["time_finished"]
|
|
if when == nil {
|
|
ret.Status = ResultCritical
|
|
ret.Message = "Tidy hasn't run since this mount was created; this can point to problems with the mount's auto-tidy configuration or an external tidy executor; this can impact PKI's and Vault's performance if not run regularly. It is suggested to enable auto-tidy on this mount."
|
|
} else {
|
|
now := time.Now()
|
|
lastRunCritical := now.Add(-1 * h.LastRunCritical)
|
|
lastRunWarning := now.Add(-1 * h.LastRunWarning)
|
|
|
|
whenT, err := parseutil.ParseAbsoluteTime(when)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error parsing time value (%v): %w", when, err)
|
|
}
|
|
|
|
if whenT.Before(lastRunCritical) {
|
|
ret.Status = ResultCritical
|
|
ret.Message = fmt.Sprintf(baseMsg, h.LastRunCritical)
|
|
} else if whenT.Before(lastRunWarning) {
|
|
ret.Status = ResultWarning
|
|
ret.Message = fmt.Sprintf(baseMsg, h.LastRunWarning)
|
|
}
|
|
}
|
|
|
|
results = append(results, &ret)
|
|
}
|
|
|
|
return
|
|
}
|