| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| package healthcheck |
| |
| import ( |
| "fmt" |
| |
| "github.com/hashicorp/go-secure-stdlib/parseutil" |
| ) |
| |
| var VisibleReqParams = []string{ |
| "csr", |
| "certificate", |
| "issuer_ref", |
| "common_name", |
| "alt_names", |
| "other_sans", |
| "ip_sans", |
| "uri_sans", |
| "ttl", |
| "not_after", |
| "serial_number", |
| "key_type", |
| "private_key_format", |
| "managed_key_name", |
| "managed_key_id", |
| "ou", |
| "organization", |
| "country", |
| "locality", |
| "province", |
| "street_address", |
| "postal_code", |
| "permitted_dns_domains", |
| "policy_identifiers", |
| "ext_key_usage_oids", |
| } |
| |
| var VisibleRespParams = []string{ |
| "certificate", |
| "issuing_ca", |
| "serial_number", |
| "error", |
| "ca_chain", |
| } |
| |
| var HiddenReqParams = []string{ |
| "private_key", |
| "pem_bundle", |
| } |
| |
| var HiddenRespParams = []string{ |
| "private_key", |
| "pem_bundle", |
| } |
| |
| type AuditVisibility struct { |
| Enabled bool |
| UnsupportedVersion bool |
| |
| IgnoredParameters map[string]bool |
| TuneData map[string]interface{} |
| Fetcher *PathFetch |
| } |
| |
| func NewAuditVisibilityCheck() Check { |
| return &AuditVisibility{ |
| IgnoredParameters: make(map[string]bool), |
| } |
| } |
| |
| func (h *AuditVisibility) Name() string { |
| return "audit_visibility" |
| } |
| |
| func (h *AuditVisibility) IsEnabled() bool { |
| return h.Enabled |
| } |
| |
| func (h *AuditVisibility) DefaultConfig() map[string]interface{} { |
| return map[string]interface{}{ |
| "ignored_parameters": []string{}, |
| } |
| } |
| |
| func (h *AuditVisibility) LoadConfig(config map[string]interface{}) error { |
| var err error |
| |
| coerced, err := StringList(config["ignored_parameters"]) |
| if err != nil { |
| return fmt.Errorf("error parsing %v.ignored_parameters: %v", h.Name(), err) |
| } |
| for _, ignored := range coerced { |
| h.IgnoredParameters[ignored] = true |
| } |
| |
| 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 *AuditVisibility) FetchResources(e *Executor) error { |
| var exit bool |
| var err error |
| |
| exit, h.Fetcher, h.TuneData, err = fetchMountTune(e, func() { |
| h.UnsupportedVersion = true |
| }) |
| |
| if exit || err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| func (h *AuditVisibility) Evaluate(e *Executor) (results []*Result, err error) { |
| if h.UnsupportedVersion { |
| ret := Result{ |
| Status: ResultInvalidVersion, |
| Endpoint: "/sys/mounts/{{mount}}/tune", |
| Message: "This health check requires Vault 1.9+ but an earlier version of Vault Server was contacted, preventing this health check from running.", |
| } |
| return []*Result{&ret}, nil |
| } |
| |
| if h.Fetcher.IsSecretPermissionsError() { |
| ret := Result{ |
| Status: ResultInsufficientPermissions, |
| Endpoint: "/sys/mounts/{{mount}}/tune", |
| Message: "Without this information, this health check is unable to function.", |
| } |
| |
| if e.Client.Token() == "" { |
| ret.Message = "No token available so unable read the tune endpoint for this mount. " + ret.Message |
| } else { |
| ret.Message = "This token lacks permission to read the tune endpoint for this mount. " + ret.Message |
| } |
| |
| results = append(results, &ret) |
| return |
| } |
| |
| sourceMap := map[string][]string{ |
| "audit_non_hmac_request_keys": VisibleReqParams, |
| "audit_non_hmac_response_keys": VisibleRespParams, |
| } |
| for source, visibleList := range sourceMap { |
| actual, err := StringList(h.TuneData[source]) |
| if err != nil { |
| return nil, fmt.Errorf("error parsing %v from server: %v", source, err) |
| } |
| |
| for _, param := range visibleList { |
| found := false |
| for _, tuned := range actual { |
| if param == tuned { |
| found = true |
| break |
| } |
| } |
| |
| if !found { |
| ret := Result{ |
| Status: ResultInformational, |
| Endpoint: "/sys/mounts/{{mount}}/tune", |
| Message: fmt.Sprintf("Mount currently HMACs %v because it is not in %v; as this is not a sensitive security parameter, it is encouraged to disable HMACing to allow better auditing of the PKI engine.", param, source), |
| } |
| results = append(results, &ret) |
| } |
| } |
| } |
| |
| sourceMap = map[string][]string{ |
| "audit_non_hmac_request_keys": HiddenReqParams, |
| "audit_non_hmac_response_keys": HiddenRespParams, |
| } |
| for source, hiddenList := range sourceMap { |
| actual, err := StringList(h.TuneData[source]) |
| if err != nil { |
| return nil, fmt.Errorf("error parsing %v from server: %v", source, err) |
| } |
| for _, param := range hiddenList { |
| found := false |
| for _, tuned := range actual { |
| if param == tuned { |
| found = true |
| break |
| } |
| } |
| |
| if found { |
| ret := Result{ |
| Status: ResultWarning, |
| Endpoint: "/sys/mounts/{{mount}}/tune", |
| Message: fmt.Sprintf("Mount currently doesn't HMAC %v because it is in %v; as this is a sensitive security parameter it is encouraged to HMAC it in the Audit logs.", param, source), |
| } |
| results = append(results, &ret) |
| } |
| } |
| } |
| |
| if len(results) == 0 { |
| ret := Result{ |
| Status: ResultOK, |
| Endpoint: "/sys/mounts/{{mount}}/tune", |
| Message: "Mount audit information is configured appropriately.", |
| } |
| results = append(results, &ret) |
| } |
| |
| return |
| } |