~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
95 lines · 2.9 KB Raw History
package vault

import (
	"context"
	"errors"
	"fmt"
	"log/slog"

	commonv1 "github.com/johnandersen777/socialweb-computer-kcp/api/common/v1alpha1"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/bus"
	contractvault "github.com/johnandersen777/socialweb-computer-kcp/internal/contract/vault"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/kit/tlsreload"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/vault/openbao"
)

var caKeys = []string{"caBundle", "ca.crt", "ca", "certificate", "value"}

type Roots struct {
	bus *bus.Bus

	log *slog.Logger

	pools tlsreload.Pools
}

func NewRoots(b *bus.Bus, log *slog.Logger) *Roots {
	if log == nil {
		log = slog.New(slog.DiscardHandler)
	}
	return &Roots{bus: b, log: log}
}

func (r *Roots) Pool(ctx context.Context, from commonv1.ObjectRef, caRef *commonv1.ObjectRef, caSecret *commonv1.SecretRef) (*tlsreload.Pool, error) {
	if caRef != nil && caRef.IsZero() {
		caRef = nil
	}
	switch {
	case caRef == nil && caSecret == nil:
		return nil, nil
	case caRef != nil && caSecret != nil:
		return nil, errors.New("vault: tls names both caRef and caSecretRef")
	case r == nil || r.bus == nil:
		return nil, errors.New("vault: the CA refs need the bus")
	}
	fingerprint := ""
	if caRef != nil {
		fingerprint = "trust:" + caRef.String()
	} else {
		to, _, _, err := secretTarget(from, *caSecret)
		if err != nil {
			return nil, err
		}
		fingerprint = "secret:" + to.String() + "/" + SecretScope(*caSecret) + "#" + caSecret.Key
	}
	return r.pools.Get(from.String(), fingerprint, func() (*tlsreload.Pool, func(), error) {
		if caRef != nil {
			to := *caRef
			to.Workspace = to.InWorkspace(from.Workspace)
			trust, err := bus.Resolve[contractvault.Trust](ctx, r.bus, from, to,
				bus.Capability{Name: contractvault.CapabilityRead, Scope: []string{contractvault.TrustScope}})
			if err != nil {
				return nil, nil, fmt.Errorf("vault: the CA bundle of %s for %s: %w", to.String(), from.String(), err)
			}
			pool, stop := tlsreload.TrustPool(ctx, trust, r.log.With("ca", to.String()))
			return pool, stop, nil
		}
		ref := *caSecret
		pool := tlsreload.SecretPool(func(ctx context.Context) ([]byte, error) {
			data, err := ReadSecret(ctx, r.bus, from, ref)
			if err != nil {
				return nil, err
			}
			value := SecretValue(data, ref.Key, caKeys...)
			if value == "" {
				return nil, fmt.Errorf("vault: the CA secret %s holds no CA bundle", SecretScope(ref))
			}
			return []byte(value), nil
		}, SecretRenew, r.log)
		return pool, func() {}, nil
	})
}

func (r *Roots) Drop(from commonv1.ObjectRef) {
	if r != nil {
		r.pools.Drop(from.String())
	}
}

func Remote(b *bus.Bus, log *slog.Logger) *openbao.Remote {
	roots := NewRoots(b, log)
	return &openbao.Remote{Enabled: true, Roots: roots.Pool, Release: roots.Drop,
		Secrets: func(ctx context.Context, from commonv1.ObjectRef, ref commonv1.SecretRef) (map[string]any, error) {
			return ReadSecret(ctx, b, from, ref)
		}}
}