116 lines
2.9 KiB
Go
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)
|
|
}
|
|
}
|