| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| package healthcheck |
| |
| import ( |
| "fmt" |
| |
| "github.com/hashicorp/go-secure-stdlib/parseutil" |
| ) |
| |
| type TooManyCerts struct { |
| Enabled bool |
| UnsupportedVersion bool |
| |
| CountCritical int |
| CountWarning int |
| |
| CertCounts int |
| FetchIssue *PathFetch |
| } |
| |
| func NewTooManyCertsCheck() Check { |
| return &TooManyCerts{} |
| } |
| |
| func (h *TooManyCerts) Name() string { |
| return "too_many_certs" |
| } |
| |
| func (h *TooManyCerts) IsEnabled() bool { |
| return h.Enabled |
| } |
| |
| func (h *TooManyCerts) DefaultConfig() map[string]interface{} { |
| return map[string]interface{}{ |
| "count_critical": 250000, |
| "count_warning": 50000, |
| } |
| } |
| |
| func (h *TooManyCerts) LoadConfig(config map[string]interface{}) error { |
| value, err := parseutil.SafeParseIntRange(config["count_critical"], 1, 15000000) |
| if err != nil { |
| return fmt.Errorf("error parsing %v.count_critical: %w", h.Name(), err) |
| } |
| h.CountCritical = int(value) |
| |
| value, err = parseutil.SafeParseIntRange(config["count_warning"], 1, 15000000) |
| if err != nil { |
| return fmt.Errorf("error parsing %v.count_warning: %w", h.Name(), err) |
| } |
| h.CountWarning = int(value) |
| |
| h.Enabled, err = parseutil.ParseBool(config["enabled"]) |
| if err != nil { |
| return fmt.Errorf("error parsing %v.enabled: %w", h.Name(), err) |
| } |
| |
| return nil |
| } |
| |
| func (h *TooManyCerts) FetchResources(e *Executor) error { |
| exit, leavesRet, _, err := pkiFetchLeavesList(e, func() { |
| h.UnsupportedVersion = true |
| }) |
| h.FetchIssue = leavesRet |
| |
| if exit || err != nil { |
| return err |
| } |
| |
| h.CertCounts = leavesRet.ParsedCache["count"].(int) |
| |
| return nil |
| } |
| |
| func (h *TooManyCerts) Evaluate(e *Executor) (results []*Result, err error) { |
| if h.UnsupportedVersion { |
| // Shouldn't happen; /certs has been around forever. |
| ret := Result{ |
| Status: ResultInvalidVersion, |
| Endpoint: "/{{mount}}/certs", |
| Message: "This health check requires Vault 1.11+ but an earlier version of Vault Server was contacted, preventing this health check from running.", |
| } |
| return []*Result{&ret}, nil |
| } |
| |
| if h.FetchIssue != nil && h.FetchIssue.IsSecretPermissionsError() { |
| ret := Result{ |
| Status: ResultInsufficientPermissions, |
| Endpoint: h.FetchIssue.Path, |
| Message: "Without this information, this health check is unable to function.", |
| } |
| |
| if e.Client.Token() == "" { |
| ret.Message = "No token available so unable to list the endpoint for this mount. " + ret.Message |
| } else { |
| ret.Message = "This token lacks permission to list the endpoint for this mount. " + ret.Message |
| } |
| |
| results = append(results, &ret) |
| return |
| } |
| |
| ret := Result{ |
| Status: ResultOK, |
| Endpoint: "/{{mount}}/certs", |
| Message: "This mount has an OK number of stored certificates.", |
| } |
| |
| baseMsg := "This PKI mount has %v outstanding stored certificates; consider using no_store=false on roles, running tidy operations periodically, and using shorter certificate lifetimes to reduce the storage pressure on this mount." |
| if h.CertCounts >= h.CountCritical { |
| ret.Status = ResultCritical |
| ret.Message = fmt.Sprintf(baseMsg, h.CertCounts) |
| } else if h.CertCounts >= h.CountWarning { |
| ret.Status = ResultWarning |
| ret.Message = fmt.Sprintf(baseMsg, h.CertCounts) |
| } |
| |
| results = append(results, &ret) |
| |
| return |
| } |