~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
126 lines · 3.6 KB Raw History
package marketnetwork

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/hex"
	"encoding/pem"
	"math/big"
	"os"
	"path/filepath"
	"time"

	"github.com/johnandersen777/socialweb-computer-kcp/api/v1alpha1"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/app/compose"
)

const (
	GuestTLSDir     = "guest-tls"
	GuestTLSHost    = v1alpha1.GuestTLSHost
	guestTLSDays    = 30
	guestCAFile     = "ca.crt"
	guestCAKeyFile  = "ca.key"
	guestLeafFile   = GuestTLSHost + ".crt"
	guestLeafKey    = GuestTLSHost + ".key"
	keysDir         = "keys"
	hexKeyBytes     = 32
	certRenewBefore = 24 * time.Hour
)

func pemBlock(kind string, der []byte) []byte {
	return pem.EncodeToMemory(&pem.Block{Type: kind, Bytes: der})
}

func serial() (*big.Int, error) {
	return rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 120))
}

func certValid(path string) bool {
	b, err := os.ReadFile(path)
	if err != nil {
		return false
	}
	blk, _ := pem.Decode(b)
	if blk == nil {
		return false
	}
	c, err := x509.ParseCertificate(blk.Bytes)
	return err == nil && time.Until(c.NotAfter) > certRenewBefore
}

func EnsureGuestTLS(dir string) error {
	if certValid(filepath.Join(dir, guestLeafFile)) && certValid(filepath.Join(dir, guestCAFile)) {
		return nil
	}
	if err := os.MkdirAll(dir, 0o700); err != nil {
		return err
	}
	now := time.Now()
	caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return err
	}
	sn, err := serial()
	if err != nil {
		return err
	}
	caTmpl := &x509.Certificate{SerialNumber: sn, Subject: pkix.Name{CommonName: "market network guest CA"},
		NotBefore: now.Add(-time.Minute), NotAfter: now.AddDate(0, 0, guestTLSDays), IsCA: true, BasicConstraintsValid: true,
		KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign}
	caDER, err := x509.CreateCertificate(rand.Reader, caTmpl, caTmpl, &caKey.PublicKey, caKey)
	if err != nil {
		return err
	}
	ca, err := x509.ParseCertificate(caDER)
	if err != nil {
		return err
	}
	leafKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return err
	}
	if sn, err = serial(); err != nil {
		return err
	}
	leafTmpl := &x509.Certificate{SerialNumber: sn, Subject: pkix.Name{CommonName: GuestTLSHost},
		NotBefore: now.Add(-time.Minute), NotAfter: now.AddDate(0, 0, guestTLSDays), DNSNames: []string{GuestTLSHost, "*." + GuestTLSHost},
		KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true}
	leafDER, err := x509.CreateCertificate(rand.Reader, leafTmpl, ca, &leafKey.PublicKey, caKey)
	if err != nil {
		return err
	}
	caKeyDER, err := x509.MarshalPKCS8PrivateKey(caKey)
	if err != nil {
		return err
	}
	leafKeyDER, err := x509.MarshalPKCS8PrivateKey(leafKey)
	if err != nil {
		return err
	}
	for name, data := range map[string][]byte{guestCAKeyFile: pemBlock("PRIVATE KEY", caKeyDER), guestLeafKey: pemBlock("PRIVATE KEY", leafKeyDER)} {
		if err := compose.WriteSecret(filepath.Join(dir, name), data); err != nil {
			return err
		}
	}
	for name, data := range map[string][]byte{guestCAFile: pemBlock("CERTIFICATE", caDER), guestLeafFile: pemBlock("CERTIFICATE", leafDER)} {
		if err := os.WriteFile(filepath.Join(dir, name), data, 0o644); err != nil {
			return err
		}
	}
	return nil
}

func EnsureHexKey(path string) (string, error) {
	if b, err := os.ReadFile(path); err == nil && len(b) == 2*hexKeyBytes {
		return string(b), nil
	}
	buf := make([]byte, hexKeyBytes)
	if _, err := rand.Read(buf); err != nil {
		return "", err
	}
	k := hex.EncodeToString(buf)
	return k, compose.WriteSecret(path, []byte(k))
}