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, } }