mirror of https://github.com/grafana/grafana
Plugins: Add sql support for the secure socks proxy (#64630)
parent
68e38aad6a
commit
10db808ea1
@ -0,0 +1,97 @@ |
|||||||
|
package proxy |
||||||
|
|
||||||
|
import ( |
||||||
|
"net/http" |
||||||
|
"os" |
||||||
|
"path/filepath" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/backend" |
||||||
|
"github.com/grafana/grafana/pkg/infra/proxy/proxyutil" |
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
) |
||||||
|
|
||||||
|
func TestNewSecureSocksProxy(t *testing.T) { |
||||||
|
settings := proxyutil.SetupTestSecureSocksProxySettings(t) |
||||||
|
|
||||||
|
// create empty file for testing invalid configs
|
||||||
|
tempDir := t.TempDir() |
||||||
|
tempEmptyFile := filepath.Join(tempDir, "emptyfile.txt") |
||||||
|
// nolint:gosec
|
||||||
|
// The gosec G304 warning can be ignored because all values come from the test
|
||||||
|
_, err := os.Create(tempEmptyFile) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
t.Run("New socks proxy should be properly configured when all settings are valid", func(t *testing.T) { |
||||||
|
require.NoError(t, NewSecureSocksHTTPProxy(settings, &http.Transport{})) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("Client cert must be valid", func(t *testing.T) { |
||||||
|
clientCertBefore := settings.ClientCert |
||||||
|
settings.ClientCert = tempEmptyFile |
||||||
|
t.Cleanup(func() { |
||||||
|
settings.ClientCert = clientCertBefore |
||||||
|
}) |
||||||
|
require.Error(t, NewSecureSocksHTTPProxy(settings, &http.Transport{})) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("Client key must be valid", func(t *testing.T) { |
||||||
|
clientKeyBefore := settings.ClientKey |
||||||
|
settings.ClientKey = tempEmptyFile |
||||||
|
t.Cleanup(func() { |
||||||
|
settings.ClientKey = clientKeyBefore |
||||||
|
}) |
||||||
|
require.Error(t, NewSecureSocksHTTPProxy(settings, &http.Transport{})) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("Root CA must be valid", func(t *testing.T) { |
||||||
|
rootCABefore := settings.RootCA |
||||||
|
settings.RootCA = tempEmptyFile |
||||||
|
t.Cleanup(func() { |
||||||
|
settings.RootCA = rootCABefore |
||||||
|
}) |
||||||
|
require.Error(t, NewSecureSocksHTTPProxy(settings, &http.Transport{})) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
func TestSecureSocksProxyEnabledOnDS(t *testing.T) { |
||||||
|
t.Run("Secure socks proxy should only be enabled when the json data contains enableSecureSocksProxy=true", func(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
instanceSettings *backend.AppInstanceSettings |
||||||
|
enabled bool |
||||||
|
}{ |
||||||
|
{ |
||||||
|
instanceSettings: &backend.AppInstanceSettings{ |
||||||
|
JSONData: []byte("{}"), |
||||||
|
}, |
||||||
|
enabled: false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
instanceSettings: &backend.AppInstanceSettings{ |
||||||
|
JSONData: []byte("{ \"enableSecureSocksProxy\": \"nonbool\" }"), |
||||||
|
}, |
||||||
|
enabled: false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
instanceSettings: &backend.AppInstanceSettings{ |
||||||
|
JSONData: []byte("{ \"enableSecureSocksProxy\": false }"), |
||||||
|
}, |
||||||
|
enabled: false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
instanceSettings: &backend.AppInstanceSettings{ |
||||||
|
JSONData: []byte("{ \"enableSecureSocksProxy\": true }"), |
||||||
|
}, |
||||||
|
enabled: true, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
for _, tt := range tests { |
||||||
|
opts, err := tt.instanceSettings.HTTPClientOptions() |
||||||
|
assert.NoError(t, err) |
||||||
|
|
||||||
|
assert.Equal(t, tt.enabled, SecureSocksProxyEnabledOnDS(opts)) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
@ -0,0 +1,91 @@ |
|||||||
|
package mssql |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"database/sql" |
||||||
|
"database/sql/driver" |
||||||
|
"errors" |
||||||
|
|
||||||
|
mssql "github.com/denisenkom/go-mssqldb" |
||||||
|
iproxy "github.com/grafana/grafana/pkg/infra/proxy" |
||||||
|
"github.com/grafana/grafana/pkg/setting" |
||||||
|
"github.com/grafana/grafana/pkg/tsdb/sqleng" |
||||||
|
"github.com/grafana/grafana/pkg/util" |
||||||
|
"golang.org/x/net/proxy" |
||||||
|
"xorm.io/core" |
||||||
|
) |
||||||
|
|
||||||
|
// createMSSQLProxyDriver creates and registers a new sql driver that uses a mssql connector and updates the dialer to
|
||||||
|
// route connections through the secure socks proxy
|
||||||
|
func createMSSQLProxyDriver(settings *setting.SecureSocksDSProxySettings, cnnstr string) (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 := "mssql-proxy-" + hash |
||||||
|
|
||||||
|
// only register the driver once
|
||||||
|
if core.QueryDriver(driverName) == nil { |
||||||
|
connector, err := mssql.NewConnector(cnnstr) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
|
||||||
|
driver, err := newMSSQLProxyDriver(settings, connector) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
sql.Register(driverName, driver) |
||||||
|
core.RegisterDriver(driverName, driver) |
||||||
|
} |
||||||
|
|
||||||
|
return driverName, nil |
||||||
|
} |
||||||
|
|
||||||
|
// mssqlProxyDriver is a regular mssql driver with an updated dialer.
|
||||||
|
// This is needed because there is no way to save a dialer to the mssql driver in xorm
|
||||||
|
type mssqlProxyDriver struct { |
||||||
|
c *mssql.Connector |
||||||
|
} |
||||||
|
|
||||||
|
var _ driver.DriverContext = (*mssqlProxyDriver)(nil) |
||||||
|
var _ core.Driver = (*mssqlProxyDriver)(nil) |
||||||
|
|
||||||
|
// newMSSQLProxyDriver updates the dialer for a mssql connector with a dialer that proxys connections through the secure socks proxy
|
||||||
|
// and returns a new mssql driver to register
|
||||||
|
func newMSSQLProxyDriver(cfg *setting.SecureSocksDSProxySettings, connector *mssql.Connector) (*mssqlProxyDriver, error) { |
||||||
|
dialer, err := iproxy.NewSecureSocksProxyContextDialer(cfg) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
contextDialer, ok := dialer.(proxy.ContextDialer) |
||||||
|
if !ok { |
||||||
|
return nil, errors.New("unable to cast socks proxy dialer to context proxy dialer") |
||||||
|
} |
||||||
|
|
||||||
|
connector.Dialer = contextDialer |
||||||
|
return &mssqlProxyDriver{c: connector}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Parse uses the xorm mssql dialect for the driver (this has to be implemented to register the driver with xorm)
|
||||||
|
func (d *mssqlProxyDriver) Parse(a string, b string) (*core.Uri, error) { |
||||||
|
sqleng.XormDriverMu.RLock() |
||||||
|
defer sqleng.XormDriverMu.RUnlock() |
||||||
|
|
||||||
|
return core.QueryDriver("mssql").Parse(a, b) |
||||||
|
} |
||||||
|
|
||||||
|
// OpenConnector returns the normal mssql connector that has the updated dialer context
|
||||||
|
func (d *mssqlProxyDriver) OpenConnector(name string) (driver.Connector, error) { |
||||||
|
return d.c, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Open uses the connector with the updated dialer context to open a new connection
|
||||||
|
func (d *mssqlProxyDriver) Open(dsn string) (driver.Conn, error) { |
||||||
|
return d.c.Connect(context.Background()) |
||||||
|
} |
||||||
@ -0,0 +1,66 @@ |
|||||||
|
package mssql |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"fmt" |
||||||
|
"testing" |
||||||
|
|
||||||
|
mssql "github.com/denisenkom/go-mssqldb" |
||||||
|
"github.com/grafana/grafana/pkg/infra/proxy/proxyutil" |
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
"xorm.io/core" |
||||||
|
) |
||||||
|
|
||||||
|
func TestMSSQLProxyDriver(t *testing.T) { |
||||||
|
settings := proxyutil.SetupTestSecureSocksProxySettings(t) |
||||||
|
dialect := "mssql" |
||||||
|
cnnstr := "server=127.0.0.1;port=1433;user id=sa;password=yourStrong(!)Password;database=db" |
||||||
|
driverName, err := createMSSQLProxyDriver(settings, cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
t.Run("Driver should not be registered more than once", func(t *testing.T) { |
||||||
|
testDriver, err := createMSSQLProxyDriver(settings, cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
require.Equal(t, driverName, testDriver) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("A new driver should be created for a new connection string", func(t *testing.T) { |
||||||
|
testDriver, err := createMSSQLProxyDriver(settings, "server=localhost;user id=sa;password=yourStrong(!)Password;database=db2") |
||||||
|
require.NoError(t, err) |
||||||
|
require.NotEqual(t, driverName, testDriver) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("Parse should have the same result as xorm mssql parse", func(t *testing.T) { |
||||||
|
xormDriver := core.QueryDriver(dialect) |
||||||
|
xormResult, err := xormDriver.Parse(dialect, cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
xormNewDriver := core.QueryDriver(driverName) |
||||||
|
xormNewResult, err := xormNewDriver.Parse(dialect, cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
require.Equal(t, xormResult, xormNewResult) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("Connector should use dialer context that routes through the socks proxy to db", func(t *testing.T) { |
||||||
|
connector, err := mssql.NewConnector(cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
driver, err := newMSSQLProxyDriver(settings, connector) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
conn, err := driver.OpenConnector(cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
_, err = conn.Connect(context.Background()) |
||||||
|
require.Contains(t, err.Error(), fmt.Sprintf("socks connect tcp %s->127.0.0.1:1433", settings.ProxyAddress)) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("Open should use the connector that routes through the socks proxy to db", func(t *testing.T) { |
||||||
|
connector, err := mssql.NewConnector(cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
driver, err := newMSSQLProxyDriver(settings, connector) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
_, err = driver.Open(cnnstr) |
||||||
|
require.Contains(t, err.Error(), fmt.Sprintf("socks connect tcp %s->127.0.0.1:1433", settings.ProxyAddress)) |
||||||
|
}) |
||||||
|
} |
||||||
@ -0,0 +1,57 @@ |
|||||||
|
package mysql |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"net" |
||||||
|
|
||||||
|
"github.com/go-sql-driver/mysql" |
||||||
|
iproxy "github.com/grafana/grafana/pkg/infra/proxy" |
||||||
|
"github.com/grafana/grafana/pkg/setting" |
||||||
|
"github.com/grafana/grafana/pkg/util" |
||||||
|
"golang.org/x/net/proxy" |
||||||
|
) |
||||||
|
|
||||||
|
// registerProxyDialerContext registers a new dialer context to be used by mysql when the proxy network is
|
||||||
|
// specified in the connection string
|
||||||
|
func registerProxyDialerContext(settings *setting.SecureSocksDSProxySettings, protocol, cnnstr string) (string, error) { |
||||||
|
// the dialer contains the true network used behind the scenes
|
||||||
|
dialer, err := getProxyDialerContext(settings, protocol) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
|
||||||
|
// the dialer context can be updated everytime the datasource is updated
|
||||||
|
// have a unique network per connection string
|
||||||
|
hash, err := util.Md5SumString(cnnstr) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
network := "proxy-" + hash |
||||||
|
mysql.RegisterDialContext(network, dialer.DialContext) |
||||||
|
|
||||||
|
return network, nil |
||||||
|
} |
||||||
|
|
||||||
|
// mySQLContextDialer turns a golang proxy driver into a MySQL proxy driver
|
||||||
|
type mySQLContextDialer struct { |
||||||
|
dialer proxy.ContextDialer |
||||||
|
network string |
||||||
|
} |
||||||
|
|
||||||
|
// getProxyDialerContext returns a context dialer that will send the request through to the secure socks proxy
|
||||||
|
func getProxyDialerContext(cfg *setting.SecureSocksDSProxySettings, actualNetwork string) (*mySQLContextDialer, error) { |
||||||
|
dialer, err := iproxy.NewSecureSocksProxyContextDialer(cfg) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
contextDialer, ok := dialer.(proxy.ContextDialer) |
||||||
|
if !ok { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &mySQLContextDialer{dialer: contextDialer, network: actualNetwork}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// DialContext implements the MySQL requirements for a proxy driver, and uses the underlying golang proxy driver with the assigned network
|
||||||
|
func (d *mySQLContextDialer) DialContext(ctx context.Context, addr string) (net.Conn, error) { |
||||||
|
return d.dialer.DialContext(ctx, d.network, addr) |
||||||
|
} |
||||||
@ -0,0 +1,51 @@ |
|||||||
|
package mysql |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"fmt" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/go-sql-driver/mysql" |
||||||
|
"github.com/grafana/grafana/pkg/infra/proxy/proxyutil" |
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
) |
||||||
|
|
||||||
|
func TestMySQLProxyDialer(t *testing.T) { |
||||||
|
settings := proxyutil.SetupTestSecureSocksProxySettings(t) |
||||||
|
|
||||||
|
protocol := "tcp" |
||||||
|
network, err := registerProxyDialerContext(settings, protocol, "1") |
||||||
|
require.NoError(t, err) |
||||||
|
driver := mysql.MySQLDriver{} |
||||||
|
dbURL := "localhost:5432" |
||||||
|
cnnstr := fmt.Sprintf("test:test@%s(%s)/db", |
||||||
|
network, |
||||||
|
dbURL, |
||||||
|
) |
||||||
|
t.Run("Network is available", func(t *testing.T) { |
||||||
|
_, err = driver.OpenConnector(cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("Multiple networks can be created", func(t *testing.T) { |
||||||
|
network, err := registerProxyDialerContext(settings, protocol, "2") |
||||||
|
require.NoError(t, err) |
||||||
|
cnnstr2 := fmt.Sprintf("test:test@%s(%s)/db", |
||||||
|
network, |
||||||
|
dbURL, |
||||||
|
) |
||||||
|
// both networks should exist
|
||||||
|
_, err = driver.OpenConnector(cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
_, err = driver.OpenConnector(cnnstr2) |
||||||
|
require.NoError(t, err) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("Connection should be routed through socks proxy to db", func(t *testing.T) { |
||||||
|
conn, err := driver.OpenConnector(cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
_, err = conn.Connect(context.Background()) |
||||||
|
require.Error(t, err) |
||||||
|
require.Contains(t, err.Error(), fmt.Sprintf("socks connect %s %s->%s", protocol, settings.ProxyAddress, dbURL)) |
||||||
|
}) |
||||||
|
} |
||||||
@ -0,0 +1,107 @@ |
|||||||
|
package postgres |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"database/sql" |
||||||
|
"database/sql/driver" |
||||||
|
"net" |
||||||
|
"time" |
||||||
|
|
||||||
|
iproxy "github.com/grafana/grafana/pkg/infra/proxy" |
||||||
|
"github.com/grafana/grafana/pkg/setting" |
||||||
|
"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(settings *setting.SecureSocksDSProxySettings, cnnstr string) (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(settings, connector) |
||||||
|
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(cfg *setting.SecureSocksDSProxySettings, connector *pq.Connector) (*postgresProxyDriver, error) { |
||||||
|
dialer, err := iproxy.NewSecureSocksProxyContextDialer(cfg) |
||||||
|
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()) |
||||||
|
} |
||||||
@ -0,0 +1,70 @@ |
|||||||
|
package postgres |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"fmt" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/infra/proxy/proxyutil" |
||||||
|
"github.com/lib/pq" |
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
"xorm.io/core" |
||||||
|
) |
||||||
|
|
||||||
|
func TestPostgresProxyDriver(t *testing.T) { |
||||||
|
dialect := "postgres" |
||||||
|
settings := proxyutil.SetupTestSecureSocksProxySettings(t) |
||||||
|
dbURL := "localhost:5432" |
||||||
|
cnnstr := fmt.Sprintf("postgres://auser:password@%s/db?sslmode=disable", dbURL) |
||||||
|
driverName, err := createPostgresProxyDriver(settings, cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
t.Run("Driver should not be registered more than once", func(t *testing.T) { |
||||||
|
testDriver, err := createPostgresProxyDriver(settings, cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
require.Equal(t, driverName, testDriver) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("A new driver should be created for a new connection string", func(t *testing.T) { |
||||||
|
testDriver, err := createPostgresProxyDriver(settings, "server=localhost;user id=sa;password=yourStrong(!)Password;database=db2") |
||||||
|
require.NoError(t, err) |
||||||
|
require.NotEqual(t, driverName, testDriver) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("Parse should have the same result as xorm mssql parse", func(t *testing.T) { |
||||||
|
xormDriver := core.QueryDriver(dialect) |
||||||
|
xormResult, err := xormDriver.Parse(dialect, cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
xormNewDriver := core.QueryDriver(driverName) |
||||||
|
xormNewResult, err := xormNewDriver.Parse(dialect, cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
require.Equal(t, xormResult, xormNewResult) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("Connector should use dialer context that routes through the socks proxy to db", func(t *testing.T) { |
||||||
|
connector, err := pq.NewConnector(cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
driver, err := newPostgresProxyDriver(settings, connector) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
conn, err := driver.OpenConnector(cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
_, err = conn.Connect(context.Background()) |
||||||
|
require.Contains(t, err.Error(), fmt.Sprintf("socks connect %s %s->%s", "tcp", settings.ProxyAddress, dbURL)) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("Connector should use dialer context that routes through the socks proxy to db", func(t *testing.T) { |
||||||
|
connector, err := pq.NewConnector(cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
driver, err := newPostgresProxyDriver(settings, connector) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
conn, err := driver.OpenConnector(cnnstr) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
_, err = conn.Connect(context.Background()) |
||||||
|
require.Contains(t, err.Error(), fmt.Sprintf("socks connect %s %s->%s", "tcp", settings.ProxyAddress, dbURL)) |
||||||
|
}) |
||||||
|
} |
||||||
Loading…
Reference in new issue