Initial vibeproxy implementation

Self-contained HTTP/HTTPS/TCP/UDP reverse proxy manager written in Go,
with SQLite storage, a Bootstrap 5 admin UI (Go templates, no JS
framework), and a REST API under /api/v1.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QTXFrNP1imCW7jtfL5SZrw
This commit is contained in:
2026-08-17 10:28:59 -05:00
co-authored by Claude Sonnet 5
commit ba1f7c4c03
105 changed files with 9142 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
package certificates
import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"os"
"path/filepath"
)
// storage manages certificate/key material on disk. Private keys are
// written with 0600 permissions and their directories with 0700; nothing
// under here is ever written to a log.
type storage struct {
root string
}
func newStorage(root string) (*storage, error) {
if err := os.MkdirAll(filepath.Join(root, "accounts"), 0o700); err != nil {
return nil, err
}
if err := os.MkdirAll(filepath.Join(root, "certs"), 0o700); err != nil {
return nil, err
}
return &storage{root: root}, nil
}
func (s *storage) certDir(name string) string {
return filepath.Join(s.root, "certs", safeName(name))
}
func safeName(name string) string {
out := make([]rune, 0, len(name))
for _, r := range name {
switch {
case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r >= '0' && r <= '9', r == '-', r == '_', r == '.':
out = append(out, r)
default:
out = append(out, '_')
}
}
if len(out) == 0 {
return "cert"
}
return string(out)
}
// SavePair writes fullchain.pem and privkey.pem for the given logical
// certificate name and returns their paths.
func (s *storage) SavePair(name string, certPEM, keyPEM []byte) (certPath, keyPath string, err error) {
dir := s.certDir(name)
if err = os.MkdirAll(dir, 0o700); err != nil {
return
}
certPath = filepath.Join(dir, "fullchain.pem")
keyPath = filepath.Join(dir, "privkey.pem")
if err = os.WriteFile(certPath, certPEM, 0o644); err != nil {
return
}
if err = os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
return
}
return
}
func (s *storage) accountKeyPath() string {
return filepath.Join(s.root, "accounts", "account.key")
}
func loadTLSCertificate(certPath, keyPath string) (*tls.Certificate, error) {
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, err
}
if len(cert.Certificate) == 0 {
return nil, errors.New("empty certificate chain")
}
leaf, err := x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return nil, err
}
cert.Leaf = leaf
return &cert, nil
}
// ValidateKeyPair checks that certPEM/keyPEM parse and form a matching
// pair before anything is written to disk.
func ValidateKeyPair(certPEM, keyPEM []byte) (*x509.Certificate, error) {
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
return nil, fmt.Errorf("certificate/key do not match: %w", err)
}
block, _ := pem.Decode(certPEM)
if block == nil {
return nil, errors.New("no PEM data found in certificate")
}
leaf, err := x509.ParseCertificate(tlsCert.Certificate[0])
if err != nil {
return nil, err
}
return leaf, nil
}