mirror of
https://github.com/grafana/grafana.git
synced 2025-02-14 09:33:34 -06:00
107 lines
3.4 KiB
Go
107 lines
3.4 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"database/sql/driver"
|
|
"net"
|
|
"time"
|
|
|
|
sdkproxy "github.com/grafana/grafana-plugin-sdk-go/backend/proxy"
|
|
"github.com/grafana/grafana/pkg/tsdb/sqleng"
|
|
"github.com/grafana/grafana/pkg/util"
|
|
"github.com/lib/pq"
|
|
"golang.org/x/net/proxy"
|
|
"xorm.io/core"
|
|
)
|
|
|
|
// createPostgresProxyDriver creates and registers a new sql driver that uses a postgres connector and updates the dialer to
|
|
// route connections through the secure socks proxy
|
|
func createPostgresProxyDriver(cnnstr string, opts *sdkproxy.Options) (string, error) {
|
|
sqleng.XormDriverMu.Lock()
|
|
defer sqleng.XormDriverMu.Unlock()
|
|
|
|
// create a unique driver per connection string
|
|
hash, err := util.Md5SumString(cnnstr)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
driverName := "postgres-proxy-" + hash
|
|
|
|
// only register the driver once
|
|
if core.QueryDriver(driverName) == nil {
|
|
connector, err := pq.NewConnector(cnnstr)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
driver, err := newPostgresProxyDriver(connector, opts)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
sql.Register(driverName, driver)
|
|
core.RegisterDriver(driverName, driver)
|
|
}
|
|
return driverName, nil
|
|
}
|
|
|
|
// postgresProxyDriver is a regular postgres driver with an updated dialer.
|
|
// This is done because there is no way to save a dialer to the postgres driver in xorm
|
|
type postgresProxyDriver struct {
|
|
c *pq.Connector
|
|
}
|
|
|
|
var _ driver.DriverContext = (*postgresProxyDriver)(nil)
|
|
var _ core.Driver = (*postgresProxyDriver)(nil)
|
|
|
|
// newPostgresProxyDriver updates the dialer for a postgres connector with a dialer that proxys connections through the secure socks proxy
|
|
// and returns a new postgres driver to register
|
|
func newPostgresProxyDriver(connector *pq.Connector, opts *sdkproxy.Options) (*postgresProxyDriver, error) {
|
|
dialer, err := sdkproxy.Cli.NewSecureSocksProxyContextDialer(opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// update the postgres dialer with the proxy dialer
|
|
connector.Dialer(&postgresProxyDialer{d: dialer})
|
|
|
|
return &postgresProxyDriver{connector}, nil
|
|
}
|
|
|
|
// postgresProxyDialer implements the postgres dialer using a proxy dialer, as their functions differ slightly
|
|
type postgresProxyDialer struct {
|
|
d proxy.Dialer
|
|
}
|
|
|
|
// Dial uses the normal proxy dial function with the updated dialer
|
|
func (p *postgresProxyDialer) Dial(network, addr string) (c net.Conn, err error) {
|
|
return p.d.Dial(network, addr)
|
|
}
|
|
|
|
// DialTimeout uses the normal postgres dial timeout function with the updated dialer
|
|
func (p *postgresProxyDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
defer cancel()
|
|
|
|
return p.d.(proxy.ContextDialer).DialContext(ctx, network, address)
|
|
}
|
|
|
|
// Parse uses the xorm postgres dialect for the driver (this has to be implemented to register the driver with xorm)
|
|
func (d *postgresProxyDriver) Parse(a string, b string) (*core.Uri, error) {
|
|
sqleng.XormDriverMu.RLock()
|
|
defer sqleng.XormDriverMu.RUnlock()
|
|
|
|
return core.QueryDriver("postgres").Parse(a, b)
|
|
}
|
|
|
|
// OpenConnector returns the normal postgres connector that has the updated dialer context
|
|
func (d *postgresProxyDriver) OpenConnector(name string) (driver.Connector, error) {
|
|
return d.c, nil
|
|
}
|
|
|
|
// Open uses the connector with the updated dialer to open a new connection
|
|
func (d *postgresProxyDriver) Open(dsn string) (driver.Conn, error) {
|
|
return d.c.Connect(context.Background())
|
|
}
|