~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
90 lines · 3.2 KB Raw History
package esovault

import (
	"context"
	"errors"
	"fmt"
	"net"
	"strconv"
	"time"

	"github.com/johnandersen777/socialweb-computer-kcp/internal/controllers/runtime/kube"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/eso"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/vault/openbao"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/vaultcert"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/vaultlayout"
)

type VaultStore struct {
	Cluster string

	DataDir string

	VaultPort int

	Client *openbao.Client

	ManifestDir string

	Namespace string
}

func StoreVaultURL(vaultPort int) string {
	return "https://" + net.JoinHostPort("127.0.0.1", strconv.Itoa(vaultPort))
}

func ApplyVaultStore(ctx context.Context, c *kube.Client, o VaultStore) (string, error) {
	if o.DataDir == "" || o.VaultPort == 0 || o.Client == nil || o.Namespace == "" {
		return "", errors.New("esovault: the vault-backed store needs the cluster's data dir, its vault's " +
			"leased port, a client logged in to it, and the tenant namespace the ExternalSecret is written into")
	}
	if err := eso.InstallOperator(ctx, c, nil); err != nil {
		return "", fmt.Errorf("%w. The External Secrets controller runs beside the control plane and "+
			"its process-group step installs the CRDs before it starts, so a tenant that serves none "+
			"of them is one whose group came up without that step", err)
	}
	vaultURL := StoreVaultURL(o.VaultPort)
	vaultTLS, err := vaultcert.EnsureServedVaultTLS(ctx, o.DataDir, o.VaultPort, "127.0.0.1")
	if err != nil {
		return "", fmt.Errorf("esovault: the store for %s would be told to dial %s, and the vault that is "+
			"running cannot present that host: %w", o.Cluster, vaultURL, err)
	}
	if err := o.Client.PutKV(ctx, vaultlayout.PathESODemo, map[string]any{"demo": "openbao:" + o.Cluster}); err != nil {
		return "", fmt.Errorf("esovault: writing the demo value the tenant's ExternalSecret reads out of "+
			"the cluster vault for %s: %w", o.Cluster, err)
	}
	cert, err := currentOrNewESOCertificate(ctx, c, o, vaultURL, vaultTLS.CAPEM)
	if err != nil {
		return "", fmt.Errorf("esovault: providing the store's client certificate for %s: %w", o.Cluster, err)
	}
	if err := eso.Apply(ctx, c, eso.Options{
		ManifestDir:   o.ManifestDir,
		Namespace:     o.Namespace,
		VaultServer:   vaultURL,
		ServerCAPEM:   vaultTLS.CAPEM,
		ClientCertPEM: cert.CertPEM,
		ClientKeyPEM:  cert.KeyPEM,
	}); err != nil {
		return "", err
	}
	return vaultURL, nil
}

func currentOrNewESOCertificate(ctx context.Context, c *kube.Client, o VaultStore, vaultURL string, caPEM []byte) (vaultcert.ClientCertificate, error) {
	heldCert, heldKey, found, err := eso.CurrentClientCertificate(ctx, c, o.Namespace)
	if err != nil {
		return vaultcert.ClientCertificate{}, err
	}
	if found && len(heldCert) > 0 && len(heldKey) > 0 {
		fresh, freshErr := vaultcert.ESOCertificateFresh(heldCert, heldKey, time.Now())
		if freshErr == nil && fresh {
			return vaultcert.ClientCertificate{
				Metadata:    vaultcert.ClientMetadata{Identity: vaultlayout.RoleESO, Role: vaultlayout.RoleESO, VaultAddr: vaultURL},
				CertPEM:     heldCert,
				KeyPEM:      heldKey,
				ServerCAPEM: caPEM,
			}, nil
		}
	}
	return vaultcert.IssueESOCertificate(ctx, o.Client, vaultURL, caPEM)
}