open-vault/command/healthcheck/pki_audit_visibility.go
Alexander Scheel 5ee7cc5e6d
Various health check improvements + tests (#18096)
* 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>
2022-11-23 14:42:19 +00:00

196 lines
4.3 KiB
Go

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{}
}
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 {
exit, _, data, err := fetchMountTune(e, func() {
h.UnsupportedVersion = true
})
if exit {
return err
}
h.TuneData = data
return nil
}
func (h *AuditVisibility) 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
}
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
}