| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| package healthcheck |
| |
| import ( |
| "crypto/x509" |
| "encoding/pem" |
| "fmt" |
| |
| "github.com/hashicorp/vault/sdk/logical" |
| ) |
| |
| func pkiFetchIssuersList(e *Executor, versionError func()) (bool, *PathFetch, []string, error) { |
| issuersRet, err := e.FetchIfNotFetched(logical.ListOperation, "/{{mount}}/issuers") |
| if err != nil { |
| return true, issuersRet, nil, err |
| } |
| |
| if !issuersRet.IsSecretOK() { |
| if issuersRet.IsUnsupportedPathError() { |
| versionError() |
| } |
| |
| if issuersRet.Is404NotFound() { |
| return true, issuersRet, nil, fmt.Errorf("this mount lacks any configured issuers, limiting health check usefulness") |
| } |
| |
| return true, issuersRet, nil, nil |
| } |
| |
| if len(issuersRet.ParsedCache) == 0 { |
| var issuers []string |
| for _, rawIssuerId := range issuersRet.Secret.Data["keys"].([]interface{}) { |
| issuers = append(issuers, rawIssuerId.(string)) |
| } |
| issuersRet.ParsedCache["issuers"] = issuers |
| } |
| |
| return false, issuersRet, issuersRet.ParsedCache["issuers"].([]string), nil |
| } |
| |
| func parsePEM(contents string) ([]byte, error) { |
| // Need to parse out the issuer from its PEM format. |
| pemBlock, _ := pem.Decode([]byte(contents)) |
| if pemBlock == nil { |
| return nil, fmt.Errorf("invalid PEM block") |
| } |
| |
| return pemBlock.Bytes, nil |
| } |
| |
| func ParsePEMCert(contents string) (*x509.Certificate, error) { |
| parsed, err := parsePEM(contents) |
| if err != nil { |
| return nil, err |
| } |
| |
| cert, err := x509.ParseCertificate(parsed) |
| if err != nil { |
| return nil, fmt.Errorf("invalid certificate: %w", err) |
| } |
| |
| return cert, nil |
| } |
| |
| func parsePEMCRL(contents string) (*x509.RevocationList, error) { |
| parsed, err := parsePEM(contents) |
| if err != nil { |
| return nil, err |
| } |
| |
| crl, err := x509.ParseRevocationList(parsed) |
| if err != nil { |
| return nil, fmt.Errorf("invalid CRL: %w", err) |
| } |
| |
| return crl, nil |
| } |
| |
| func pkiFetchIssuer(e *Executor, issuer string, versionError func()) (bool, *PathFetch, *x509.Certificate, error) { |
| issuerRet, err := e.FetchIfNotFetched(logical.ReadOperation, "/{{mount}}/issuer/"+issuer+"/json") |
| if err != nil { |
| return true, issuerRet, nil, err |
| } |
| |
| if !issuerRet.IsSecretOK() { |
| if issuerRet.IsUnsupportedPathError() { |
| versionError() |
| } |
| return true, issuerRet, nil, nil |
| } |
| |
| if len(issuerRet.ParsedCache) == 0 { |
| cert, err := ParsePEMCert(issuerRet.Secret.Data["certificate"].(string)) |
| if err != nil { |
| return true, issuerRet, nil, fmt.Errorf("unable to parse issuer %v's certificate: %w", issuer, err) |
| } |
| |
| issuerRet.ParsedCache["certificate"] = cert |
| } |
| |
| return false, issuerRet, issuerRet.ParsedCache["certificate"].(*x509.Certificate), nil |
| } |
| |
| func pkiFetchIssuerEntry(e *Executor, issuer string, versionError func()) (bool, *PathFetch, map[string]interface{}, error) { |
| issuerRet, err := e.FetchIfNotFetched(logical.ReadOperation, "/{{mount}}/issuer/"+issuer) |
| if err != nil { |
| return true, issuerRet, nil, err |
| } |
| |
| if !issuerRet.IsSecretOK() { |
| if issuerRet.IsUnsupportedPathError() { |
| versionError() |
| } |
| return true, issuerRet, nil, nil |
| } |
| |
| if len(issuerRet.ParsedCache) == 0 { |
| cert, err := ParsePEMCert(issuerRet.Secret.Data["certificate"].(string)) |
| if err != nil { |
| return true, issuerRet, nil, fmt.Errorf("unable to parse issuer %v's certificate: %w", issuer, err) |
| } |
| |
| issuerRet.ParsedCache["certificate"] = cert |
| } |
| |
| var data map[string]interface{} = nil |
| if issuerRet.Secret != nil && len(issuerRet.Secret.Data) > 0 { |
| data = issuerRet.Secret.Data |
| } |
| |
| return false, issuerRet, data, nil |
| } |
| |
| func pkiFetchIssuerCRL(e *Executor, issuer string, delta bool, versionError func()) (bool, *PathFetch, *x509.RevocationList, error) { |
| path := "/{{mount}}/issuer/" + issuer + "/crl" |
| name := "CRL" |
| if delta { |
| path += "/delta" |
| name = "Delta CRL" |
| } |
| |
| crlRet, err := e.FetchIfNotFetched(logical.ReadOperation, path) |
| if err != nil { |
| return true, crlRet, nil, err |
| } |
| |
| if !crlRet.IsSecretOK() { |
| if crlRet.IsUnsupportedPathError() { |
| versionError() |
| } |
| return true, crlRet, nil, nil |
| } |
| |
| if len(crlRet.ParsedCache) == 0 { |
| crl, err := parsePEMCRL(crlRet.Secret.Data["crl"].(string)) |
| if err != nil { |
| return true, crlRet, nil, fmt.Errorf("unable to parse issuer %v's %v: %w", issuer, name, err) |
| } |
| crlRet.ParsedCache["crl"] = crl |
| } |
| |
| return false, crlRet, crlRet.ParsedCache["crl"].(*x509.RevocationList), nil |
| } |
| |
| func pkiFetchKeyEntry(e *Executor, key string, versionError func()) (bool, *PathFetch, map[string]interface{}, error) { |
| keyRet, err := e.FetchIfNotFetched(logical.ReadOperation, "/{{mount}}/key/"+key) |
| if err != nil { |
| return true, keyRet, nil, err |
| } |
| |
| if !keyRet.IsSecretOK() { |
| if keyRet.IsUnsupportedPathError() { |
| versionError() |
| } |
| return true, keyRet, nil, nil |
| } |
| |
| var data map[string]interface{} = nil |
| if keyRet.Secret != nil && len(keyRet.Secret.Data) > 0 { |
| data = keyRet.Secret.Data |
| } |
| |
| return false, keyRet, data, nil |
| } |
| |
| func pkiFetchLeavesList(e *Executor, versionError func()) (bool, *PathFetch, []string, error) { |
| leavesRet, err := e.FetchIfNotFetched(logical.ListOperation, "/{{mount}}/certs") |
| if err != nil { |
| return true, leavesRet, nil, err |
| } |
| |
| if !leavesRet.IsSecretOK() { |
| if leavesRet.IsUnsupportedPathError() { |
| versionError() |
| } |
| |
| return true, leavesRet, nil, nil |
| } |
| |
| if len(leavesRet.ParsedCache) == 0 { |
| var leaves []string |
| for _, rawSerial := range leavesRet.Secret.Data["keys"].([]interface{}) { |
| leaves = append(leaves, rawSerial.(string)) |
| } |
| leavesRet.ParsedCache["leaves"] = leaves |
| leavesRet.ParsedCache["count"] = len(leaves) |
| } |
| |
| return false, leavesRet, leavesRet.ParsedCache["leaves"].([]string), nil |
| } |
| |
| func pkiFetchLeaf(e *Executor, serial string, versionError func()) (bool, *PathFetch, *x509.Certificate, error) { |
| leafRet, err := e.FetchIfNotFetched(logical.ReadOperation, "/{{mount}}/cert/"+serial) |
| if err != nil { |
| return true, leafRet, nil, err |
| } |
| |
| if !leafRet.IsSecretOK() { |
| if leafRet.IsUnsupportedPathError() { |
| versionError() |
| } |
| return true, leafRet, nil, nil |
| } |
| |
| if len(leafRet.ParsedCache) == 0 { |
| cert, err := ParsePEMCert(leafRet.Secret.Data["certificate"].(string)) |
| if err != nil { |
| return true, leafRet, nil, fmt.Errorf("unable to parse leaf %v's certificate: %w", serial, err) |
| } |
| |
| leafRet.ParsedCache["certificate"] = cert |
| } |
| |
| return false, leafRet, leafRet.ParsedCache["certificate"].(*x509.Certificate), nil |
| } |
| |
| func pkiFetchRolesList(e *Executor, versionError func()) (bool, *PathFetch, []string, error) { |
| rolesRet, err := e.FetchIfNotFetched(logical.ListOperation, "/{{mount}}/roles") |
| if err != nil { |
| return true, rolesRet, nil, err |
| } |
| |
| if !rolesRet.IsSecretOK() { |
| if rolesRet.IsUnsupportedPathError() { |
| versionError() |
| } |
| |
| return true, rolesRet, nil, nil |
| } |
| |
| if len(rolesRet.ParsedCache) == 0 { |
| var roles []string |
| for _, roleName := range rolesRet.Secret.Data["keys"].([]interface{}) { |
| roles = append(roles, roleName.(string)) |
| } |
| rolesRet.ParsedCache["roles"] = roles |
| } |
| |
| return false, rolesRet, rolesRet.ParsedCache["roles"].([]string), nil |
| } |
| |
| func pkiFetchRole(e *Executor, name string, versionError func()) (bool, *PathFetch, map[string]interface{}, error) { |
| roleRet, err := e.FetchIfNotFetched(logical.ReadOperation, "/{{mount}}/roles/"+name) |
| if err != nil { |
| return true, roleRet, nil, err |
| } |
| |
| if !roleRet.IsSecretOK() { |
| if roleRet.IsUnsupportedPathError() { |
| versionError() |
| } |
| return true, roleRet, nil, nil |
| } |
| |
| var data map[string]interface{} = nil |
| if roleRet.Secret != nil && len(roleRet.Secret.Data) > 0 { |
| data = roleRet.Secret.Data |
| } |
| |
| return false, roleRet, data, nil |
| } |