115 lines
3.2 KiB
Go
115 lines
3.2 KiB
Go
package proxy
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"os"
|
|
|
|
"git.secnex.io/secnex/masterlog"
|
|
"git.secnex.io/secnex/pgproxy/config"
|
|
"github.com/caddyserver/certmagic"
|
|
)
|
|
|
|
// setupLetsEncrypt configures certmagic for Let's Encrypt certificate management
|
|
func setupLetsEncrypt(config *config.Config) (*certmagic.Config, error) {
|
|
// Get hostnames from mappings
|
|
hostnames := make([]string, 0, len(config.Mappings))
|
|
for _, mapping := range config.Mappings {
|
|
hostnames = append(hostnames, mapping.External)
|
|
}
|
|
|
|
if len(hostnames) == 0 {
|
|
return nil, fmt.Errorf("no hostnames configured for Let's Encrypt")
|
|
}
|
|
|
|
// Set cache directory
|
|
cacheDir := config.TLS.LetsEncrypt.CacheDir
|
|
if cacheDir == "" {
|
|
cacheDir = "./certs/letsencrypt"
|
|
}
|
|
|
|
// Ensure cache directory exists
|
|
if err := os.MkdirAll(cacheDir, 0700); err != nil {
|
|
return nil, fmt.Errorf("failed to create cache directory: %w", err)
|
|
}
|
|
|
|
// Configure certmagic
|
|
magic := certmagic.NewDefault()
|
|
magic.Storage = &certmagic.FileStorage{Path: cacheDir}
|
|
|
|
// Set email for Let's Encrypt registration
|
|
email := config.TLS.LetsEncrypt.Email
|
|
if email == "" {
|
|
// Use a default email if not provided
|
|
email = "admin@" + hostnames[0]
|
|
masterlog.Info("Using default email for Let's Encrypt", map[string]interface{}{
|
|
"email": email,
|
|
})
|
|
}
|
|
|
|
// Configure ACME issuer
|
|
acmeIssuer := certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{
|
|
Email: email,
|
|
Agreed: true,
|
|
})
|
|
|
|
// Use staging environment if configured
|
|
if config.TLS.LetsEncrypt.Staging {
|
|
acmeIssuer.CA = certmagic.LetsEncryptStagingCA
|
|
masterlog.Info("Using Let's Encrypt staging environment", map[string]interface{}{})
|
|
} else {
|
|
acmeIssuer.CA = certmagic.LetsEncryptProductionCA
|
|
}
|
|
|
|
magic.Issuers = []certmagic.Issuer{acmeIssuer}
|
|
|
|
// Obtain certificates for all hostnames
|
|
masterlog.Info("Obtaining Let's Encrypt certificates", map[string]interface{}{
|
|
"hostnames": hostnames,
|
|
"email": email,
|
|
"staging": config.TLS.LetsEncrypt.Staging,
|
|
})
|
|
|
|
// Obtain certificates synchronously (this will block until certificates are obtained)
|
|
ctx := context.Background()
|
|
err := magic.ManageSync(ctx, hostnames)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to obtain Let's Encrypt certificates: %w", err)
|
|
}
|
|
|
|
masterlog.Info("Successfully obtained Let's Encrypt certificates", map[string]interface{}{
|
|
"hostnames": hostnames,
|
|
})
|
|
|
|
return magic, nil
|
|
}
|
|
|
|
// getCertificateForMappingsWithLetsEncrypt gets certificates using Let's Encrypt
|
|
func getCertificateForMappingsWithLetsEncrypt(config *config.Config) (*certmagic.Config, error) {
|
|
magic, err := setupLetsEncrypt(config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return magic, nil
|
|
}
|
|
|
|
// createTLSConfigWithLetsEncrypt creates a TLS config that uses certmagic for certificate management
|
|
func createTLSConfigWithLetsEncrypt(magic *certmagic.Config) *tls.Config {
|
|
return &tls.Config{
|
|
GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
|
cert, err := magic.GetCertificate(clientHello)
|
|
if err != nil {
|
|
masterlog.Error("Failed to get certificate from Let's Encrypt", map[string]interface{}{
|
|
"error": err,
|
|
"serverName": clientHello.ServerName,
|
|
})
|
|
return nil, err
|
|
}
|
|
return cert, nil
|
|
},
|
|
ClientAuth: tls.NoClientCert,
|
|
}
|
|
}
|