mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-29 17:52:32 +00:00 
			
		
		
		
	 a61174815f
			
		
	
	a61174815f
	
	
	
		
			
			* wip * Add cached OCSP client support to Cert Auth * ->pointer * Code cleanup * Fix unit tests * Use an LRU cache, and only persist up to 1000 of the most recently used values to stay under the storage entry limit * Fix caching, add fail open mode parameter to cert auth roles * reduce logging * Add the retry client and GET then POST logic * Drop persisted cache, make cache size configurable, allow for parallel testing of multiple servers * dead code * Update builtin/credential/cert/path_certs.go Co-authored-by: Alexander Scheel <alex.scheel@hashicorp.com> * Hook invalidate to reinit the ocsp cache size * locking * Conditionally init the ocsp client * Remove cache size config from cert configs, it's a backend global * Add field * Remove strangely complex validity logic * Address more feedback * Rework error returning logic * More edge cases * MORE edge cases * Add a test matrix with a builtin responder * changelog * Use an atomic for configUpdated * Actually use ocsp_enabled, and bind to a random port for testing * Update builtin/credential/cert/path_login.go Co-authored-by: Alexander Scheel <alex.scheel@hashicorp.com> * Refactor unit tests * Add status to cache * Make some functions private * Rename for testing, and attribute * Up to date gofumpt * remove hash from key, and disable the vault dependent unit test * Comment out TestMultiOCSP * imports * more imports * Address semgrep results * Attempt to pass some sort of logging to test_responder * fix overzealous search&replace Co-authored-by: Alexander Scheel <alex.scheel@hashicorp.com>
		
			
				
	
	
		
			159 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package cert
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"crypto/x509"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"net/http"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 	"sync/atomic"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/hashicorp/go-hclog"
 | |
| 	"github.com/hashicorp/go-multierror"
 | |
| 	"github.com/hashicorp/vault/sdk/framework"
 | |
| 	"github.com/hashicorp/vault/sdk/helper/ocsp"
 | |
| 	"github.com/hashicorp/vault/sdk/logical"
 | |
| )
 | |
| 
 | |
| func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
 | |
| 	b := Backend()
 | |
| 	if err := b.Setup(ctx, conf); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	bConf, err := b.Config(ctx, conf.StorageView)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if bConf != nil {
 | |
| 		b.updatedConfig(bConf)
 | |
| 	}
 | |
| 	if err := b.lockThenpopulateCRLs(ctx, conf.StorageView); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return b, nil
 | |
| }
 | |
| 
 | |
| func Backend() *backend {
 | |
| 	var b backend
 | |
| 	b.Backend = &framework.Backend{
 | |
| 		Help: backendHelp,
 | |
| 		PathsSpecial: &logical.Paths{
 | |
| 			Unauthenticated: []string{
 | |
| 				"login",
 | |
| 			},
 | |
| 		},
 | |
| 		Paths: []*framework.Path{
 | |
| 			pathConfig(&b),
 | |
| 			pathLogin(&b),
 | |
| 			pathListCerts(&b),
 | |
| 			pathCerts(&b),
 | |
| 			pathListCRLs(&b),
 | |
| 			pathCRLs(&b),
 | |
| 		},
 | |
| 		AuthRenew:    b.pathLoginRenew,
 | |
| 		Invalidate:   b.invalidate,
 | |
| 		BackendType:  logical.TypeCredential,
 | |
| 		PeriodicFunc: b.updateCRLs,
 | |
| 	}
 | |
| 
 | |
| 	b.crlUpdateMutex = &sync.RWMutex{}
 | |
| 	return &b
 | |
| }
 | |
| 
 | |
| type backend struct {
 | |
| 	*framework.Backend
 | |
| 	MapCertId *framework.PathMap
 | |
| 
 | |
| 	crls            map[string]CRLInfo
 | |
| 	crlUpdateMutex  *sync.RWMutex
 | |
| 	ocspClientMutex sync.RWMutex
 | |
| 	ocspClient      *ocsp.Client
 | |
| 	configUpdated   atomic.Bool
 | |
| }
 | |
| 
 | |
| func (b *backend) invalidate(_ context.Context, key string) {
 | |
| 	switch {
 | |
| 	case strings.HasPrefix(key, "crls/"):
 | |
| 		b.crlUpdateMutex.Lock()
 | |
| 		defer b.crlUpdateMutex.Unlock()
 | |
| 		b.crls = nil
 | |
| 	case key == "config":
 | |
| 		b.configUpdated.Store(true)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (b *backend) initOCSPClient(cacheSize int) {
 | |
| 	b.ocspClient = ocsp.New(func() hclog.Logger {
 | |
| 		return b.Logger()
 | |
| 	}, cacheSize)
 | |
| }
 | |
| 
 | |
| func (b *backend) updatedConfig(config *config) error {
 | |
| 	b.ocspClientMutex.Lock()
 | |
| 	defer b.ocspClientMutex.Unlock()
 | |
| 	b.initOCSPClient(config.OcspCacheSize)
 | |
| 	b.configUpdated.Store(false)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (b *backend) fetchCRL(ctx context.Context, storage logical.Storage, name string, crl *CRLInfo) error {
 | |
| 	response, err := http.Get(crl.CDP.Url)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if response.StatusCode == http.StatusOK {
 | |
| 		body, err := io.ReadAll(response.Body)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		certList, err := x509.ParseCRL(body)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		crl.CDP.ValidUntil = certList.TBSCertList.NextUpdate
 | |
| 		return b.setCRL(ctx, storage, certList, name, crl.CDP)
 | |
| 	}
 | |
| 	return fmt.Errorf("unexpected response code %d fetching CRL from %s", response.StatusCode, crl.CDP.Url)
 | |
| }
 | |
| 
 | |
| func (b *backend) updateCRLs(ctx context.Context, req *logical.Request) error {
 | |
| 	b.crlUpdateMutex.Lock()
 | |
| 	defer b.crlUpdateMutex.Unlock()
 | |
| 	var errs *multierror.Error
 | |
| 	for name, crl := range b.crls {
 | |
| 		if crl.CDP != nil && time.Now().After(crl.CDP.ValidUntil) {
 | |
| 			if err := b.fetchCRL(ctx, req.Storage, name, &crl); err != nil {
 | |
| 				errs = multierror.Append(errs, err)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return errs.ErrorOrNil()
 | |
| }
 | |
| 
 | |
| func (b *backend) storeConfig(ctx context.Context, storage logical.Storage, config *config) error {
 | |
| 	entry, err := logical.StorageEntryJSON("config", config)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if err := storage.Put(ctx, entry); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	b.updatedConfig(config)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| const backendHelp = `
 | |
| The "cert" credential provider allows authentication using
 | |
| TLS client certificates. A client connects to Vault and uses
 | |
| the "login" endpoint to generate a client token.
 | |
| 
 | |
| Trusted certificates are configured using the "certs/" endpoint
 | |
| by a user with root access. A certificate authority can be trusted,
 | |
| which permits all keys signed by it. Alternatively, self-signed
 | |
| certificates can be trusted avoiding the need for a CA.
 | |
| `
 |