feat(ssl): Add LetsEncrypt certificate option
This commit is contained in:
114
app/proxy/letsencrypt.go
Normal file
114
app/proxy/letsencrypt.go
Normal file
@@ -0,0 +1,114 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user