Files
drip/internal/server/tls/autocert.go

74 lines
1.7 KiB
Go

package tls
import (
"crypto/tls"
"net/http"
"os"
"path/filepath"
"go.uber.org/zap"
"golang.org/x/crypto/acme/autocert"
)
// AutoCertManager manages automatic certificate provisioning with Let's Encrypt
type AutoCertManager struct {
manager *autocert.Manager
logger *zap.Logger
}
// NewAutoCertManager creates a new AutoCert manager
func NewAutoCertManager(domain, cacheDir string, logger *zap.Logger) *AutoCertManager {
m := &autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(domain, "*."+domain),
Cache: autocert.DirCache(cacheDir),
}
logger.Info("AutoTLS enabled",
zap.String("domain", domain),
zap.String("cache_dir", cacheDir),
)
return &AutoCertManager{
manager: m,
logger: logger,
}
}
// GetTLSConfig returns the TLS configuration
func (a *AutoCertManager) GetTLSConfig() *tls.Config {
return a.manager.TLSConfig()
}
// HTTPHandler returns the HTTP handler for ACME challenges
func (a *AutoCertManager) HTTPHandler() http.Handler {
return a.manager.HTTPHandler(nil)
}
// GetCertificate gets a certificate for the given ClientHelloInfo
func (a *AutoCertManager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
cert, err := a.manager.GetCertificate(hello)
if err != nil {
a.logger.Error("Failed to get certificate",
zap.String("server_name", hello.ServerName),
zap.Error(err),
)
return nil, err
}
a.logger.Debug("Certificate obtained",
zap.String("server_name", hello.ServerName),
)
return cert, nil
}
// DefaultCacheDir returns the default cache directory for certificates
func DefaultCacheDir() string {
home := os.Getenv("HOME")
if home == "" {
home = "/tmp"
}
return filepath.Join(home, ".drip", "certs")
}