Files
pgproxy/app/proxy/proxy.go
2025-12-16 14:15:16 +01:00

116 lines
2.9 KiB
Go

package proxy
import (
"crypto/tls"
"fmt"
"net"
"git.secnex.io/secnex/masterlog"
"git.secnex.io/secnex/pgproxy/config"
"github.com/caddyserver/certmagic"
)
// Proxy handles PostgreSQL connection proxying
type Proxy struct {
listener net.Listener
config *config.Config
mappings map[string]struct {
host string
port int
}
certmagic *certmagic.Config // For Let's Encrypt certificate management
certificate *tls.Certificate // For regular TLS (non-Let's Encrypt)
}
// NewProxy creates a new proxy instance
func NewProxy(config *config.Config) (*Proxy, error) {
addr := fmt.Sprintf("%s:%d", config.Listen.Address, config.Listen.Port)
var listener net.Listener
var err error
var certmagicConfig *certmagic.Config
var certificate *tls.Certificate
// Always create a TCP listener (not TLS listener)
// We'll handle TLS in handleConnection to support both TLS and non-TLS connections
tcpListener, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
// If TLS is enabled, prepare certificates but don't wrap listener in TLS
// This allows both TLS and non-TLS connections
if config.TLS.Enabled {
// Check if Let's Encrypt is enabled
if config.TLS.LetsEncrypt.Enabled {
// Setup Let's Encrypt
certmagicConfig, err = getCertificateForMappingsWithLetsEncrypt(config)
if err != nil {
return nil, fmt.Errorf("failed to setup Let's Encrypt: %w", err)
}
masterlog.Info("TLS enabled with Let's Encrypt (optional)", map[string]interface{}{
"email": config.TLS.LetsEncrypt.Email,
"staging": config.TLS.LetsEncrypt.Staging,
"cacheDir": config.TLS.LetsEncrypt.CacheDir,
})
} else {
// Get certificate (load from files or generate self-signed)
cert, err := getCertificateForMappings(config)
if err != nil {
return nil, fmt.Errorf("failed to get TLS certificate: %w", err)
}
certificate = &cert
masterlog.Info("TLS enabled (optional)", map[string]interface{}{
"certFile": config.TLS.CertFile,
"keyFile": config.TLS.KeyFile,
})
}
}
listener = tcpListener
// Build mappings map for quick lookup
mappings := make(map[string]struct {
host string
port int
})
for _, mapping := range config.Mappings {
port := mapping.Port
if port == 0 {
port = 5432 // Default PostgreSQL port
}
mappings[mapping.External] = struct {
host string
port int
}{
host: mapping.Internal,
port: port,
}
}
return &Proxy{
listener: listener,
config: config,
mappings: mappings,
certmagic: certmagicConfig,
certificate: certificate,
}, nil
}
// Start starts the proxy server
func (p *Proxy) Start() error {
masterlog.Info("PostgreSQL proxy listening", map[string]interface{}{"address": p.config.Listen.Address, "port": p.config.Listen.Port})
for {
conn, err := p.listener.Accept()
if err != nil {
masterlog.Error("Error accepting connection", map[string]interface{}{"error": err})
continue
}
go p.handleConnection(conn)
}
}