From 4ece133fce004a5b6ee71eff6a8385c1c5765547 Mon Sep 17 00:00:00 2001 From: Stephanie Hingtgen Date: Tue, 18 Jul 2023 16:23:02 -0500 Subject: [PATCH] Secure socks proxy: use Grafana Plugin SDK (#71616) --- .github/CODEOWNERS | 2 +- go.mod | 4 +- go.sum | 6 ++ .../http_client_provider.go | 8 -- pkg/infra/proxy/secure_socks_proxy.go | 81 ----------------- pkg/infra/proxy/secure_socks_proxy_test.go | 91 ------------------- pkg/setting/setting_secure_socks_proxy.go | 17 ++++ pkg/tsdb/mssql/mssql.go | 7 +- pkg/tsdb/mssql/proxy.go | 11 +-- pkg/tsdb/mssql/proxy_test.go | 14 +-- pkg/tsdb/mysql/mysql.go | 7 +- pkg/tsdb/mysql/proxy.go | 11 +-- pkg/tsdb/mysql/proxy_test.go | 10 +- pkg/tsdb/postgres/postgres.go | 7 +- pkg/tsdb/postgres/proxy.go | 11 +-- pkg/tsdb/postgres/proxy_test.go | 14 +-- .../sqleng/proxyutil/proxy_test_util.go} | 11 ++- pkg/tsdb/sqleng/proxyutil/proxy_util.go | 15 +++ 18 files changed, 101 insertions(+), 226 deletions(-) delete mode 100644 pkg/infra/proxy/secure_socks_proxy.go delete mode 100644 pkg/infra/proxy/secure_socks_proxy_test.go rename pkg/{infra/proxy/proxyutil/proxyutil.go => tsdb/sqleng/proxyutil/proxy_test_util.go} (91%) create mode 100644 pkg/tsdb/sqleng/proxyutil/proxy_util.go diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fddfe32e3c3..aa7d7ea6626 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -87,7 +87,6 @@ /pkg/infra/metrics/ @grafana/backend-platform /pkg/infra/network/ @grafana/backend-platform /pkg/infra/process/ @grafana/backend-platform -/pkg/infra/proxy/ @grafana/hosted-grafana-team /pkg/infra/remotecache/ @grafana/backend-platform /pkg/infra/serverlock/ @grafana/backend-platform /pkg/infra/slugify/ @grafana/backend-platform @@ -147,6 +146,7 @@ /pkg/tsdb/legacydata/ @grafana/backend-platform /pkg/tsdb/opentsdb/ @grafana/backend-platform /pkg/tsdb/sqleng/ @grafana/backend-platform +/pkg/tsdb/sqleng/proxyutil @grafana/hosted-grafana-team /pkg/util/ @grafana/backend-platform /pkg/web/ @grafana/backend-platform diff --git a/go.mod b/go.mod index 491d6dea8ed..1763048e56c 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/grafana/cuetsy v0.1.10 // @grafana/grafana-as-code github.com/grafana/grafana-aws-sdk v0.15.0 // @grafana/aws-datasources github.com/grafana/grafana-azure-sdk-go v1.7.0 // @grafana/backend-platform - github.com/grafana/grafana-plugin-sdk-go v0.170.0 // @grafana/plugins-platform-backend + github.com/grafana/grafana-plugin-sdk-go v0.171.0 // @grafana/plugins-platform-backend github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // @grafana/backend-platform github.com/hashicorp/go-hclog v1.5.0 // @grafana/plugins-platform-backend github.com/hashicorp/go-plugin v1.4.9 // @grafana/plugins-platform-backend @@ -110,7 +110,7 @@ require ( go.opentelemetry.io/otel/trace v1.16.0 // @grafana/backend-platform golang.org/x/crypto v0.11.0 // @grafana/backend-platform golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // @grafana/alerting-squad-backend - golang.org/x/net v0.10.0 // @grafana/grafana-bi-squad + golang.org/x/net v0.12.0 // @grafana/grafana-bi-squad golang.org/x/oauth2 v0.8.0 // @grafana/grafana-authnz-team golang.org/x/sync v0.3.0 // @grafana/alerting-squad-backend golang.org/x/time v0.3.0 // @grafana/backend-platform diff --git a/go.sum b/go.sum index 857f9243e4a..e41cac64f03 100644 --- a/go.sum +++ b/go.sum @@ -599,6 +599,7 @@ github.com/elazarl/goproxy v0.0.0-20220115173737-adb46da277ac h1:XDAn206aIqKPdF5 github.com/elazarl/goproxy v0.0.0-20220115173737-adb46da277ac/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/elazarl/goproxy/ext v0.0.0-20220115173737-adb46da277ac h1:9yrT5tmn9Zc0ytWPASlaPwQfQMQYnRf0RSDe1XvHw0Q= +github.com/elazarl/goproxy/ext v0.0.0-20220115173737-adb46da277ac/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= @@ -1342,6 +1343,8 @@ github.com/grafana/grafana-plugin-sdk-go v0.165.0 h1:PTCW1bSlqPr5/k/9oz4R8NLZIiQ github.com/grafana/grafana-plugin-sdk-go v0.165.0/go.mod h1:dPhljkVno3Bg/ZYafMrR/BfYjtCRJD2hU2719Nl3QzM= github.com/grafana/grafana-plugin-sdk-go v0.170.0 h1:6vT+AcruJ7do4uISu+sXJCB8DSJh85SWNzClyM1caOU= github.com/grafana/grafana-plugin-sdk-go v0.170.0/go.mod h1:4jbsFzZvseLhsEmsPhI18fZG40mkJ2xIKPj9UopNz/8= +github.com/grafana/grafana-plugin-sdk-go v0.171.0 h1:f4W4sTgm3zJzh5EewTORMoKax4PorCmNeqINzIZ0mJw= +github.com/grafana/grafana-plugin-sdk-go v0.171.0/go.mod h1:SvYXsAQWSuV9TtHqXZJdOFCaC8G0q02U+IHWMTKIGIQ= github.com/grafana/kindsys v0.0.0-20230508162304-452481b63482 h1:1YNoeIhii4UIIQpCPU+EXidnqf449d0C3ZntAEt4KSo= github.com/grafana/kindsys v0.0.0-20230508162304-452481b63482/go.mod h1:GNcfpy5+SY6RVbNGQW264gC0r336Dm+0zgQ5vt6+M8Y= github.com/grafana/phlare/api v0.1.4-0.20230426005640-f90edba05413 h1:bBzCezZNRyYlJpXTkyZdY4fpPxHZUdyeyRWzhtw/P6I= @@ -2782,6 +2785,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181003184128-c57b0facaced/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -2812,6 +2817,7 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= diff --git a/pkg/infra/httpclient/httpclientprovider/http_client_provider.go b/pkg/infra/httpclient/httpclientprovider/http_client_provider.go index 925122d94cf..16c7b197049 100644 --- a/pkg/infra/httpclient/httpclientprovider/http_client_provider.go +++ b/pkg/infra/httpclient/httpclientprovider/http_client_provider.go @@ -9,7 +9,6 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/metrics/metricutil" - "github.com/grafana/grafana/pkg/infra/proxy" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/validations" "github.com/grafana/grafana/pkg/setting" @@ -54,13 +53,6 @@ func New(cfg *setting.Cfg, validator validations.PluginRequestValidator, tracer return } - if cfg.SecureSocksDSProxy.Enabled && proxy.SecureSocksProxyEnabledOnDS(opts) { - err = proxy.NewSecureSocksHTTPProxy(&cfg.SecureSocksDSProxy, transport) - if err != nil { - logger.Error("Failed to enable secure socks proxy", "error", err.Error(), "datasource", datasourceName) - } - } - newConntrackRoundTripper(datasourceLabelName, transport) }, }) diff --git a/pkg/infra/proxy/secure_socks_proxy.go b/pkg/infra/proxy/secure_socks_proxy.go deleted file mode 100644 index 3827c7da97c..00000000000 --- a/pkg/infra/proxy/secure_socks_proxy.go +++ /dev/null @@ -1,81 +0,0 @@ -package proxy - -import ( - "crypto/tls" - "crypto/x509" - "errors" - "net/http" - "os" - "strings" - - "github.com/grafana/grafana-plugin-sdk-go/backend" - sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" - "github.com/grafana/grafana/pkg/setting" - "golang.org/x/net/proxy" -) - -// NewSecureSocksHTTPProxy takes a http.DefaultTransport and wraps it in a socks5 proxy with TLS -func NewSecureSocksHTTPProxy(cfg *setting.SecureSocksDSProxySettings, transport *http.Transport) error { - dialSocksProxy, err := NewSecureSocksProxyContextDialer(cfg) - if err != nil { - return err - } - - contextDialer, ok := dialSocksProxy.(proxy.ContextDialer) - if !ok { - return errors.New("unable to cast socks proxy dialer to context proxy dialer") - } - - transport.DialContext = contextDialer.DialContext - return nil -} - -// NewSecureSocksProxyContextDialer returns a proxy context dialer that will wrap connections in a secure socks proxy -func NewSecureSocksProxyContextDialer(cfg *setting.SecureSocksDSProxySettings) (proxy.Dialer, error) { - certPool := x509.NewCertPool() - for _, rootCAFile := range strings.Split(cfg.RootCA, " ") { - // nolint:gosec - // The gosec G304 warning can be ignored because `rootCAFile` comes from config ini. - pem, err := os.ReadFile(rootCAFile) - if err != nil { - return nil, err - } - if !certPool.AppendCertsFromPEM(pem) { - return nil, errors.New("failed to append CA certificate " + rootCAFile) - } - } - - cert, err := tls.LoadX509KeyPair(cfg.ClientCert, cfg.ClientKey) - if err != nil { - return nil, err - } - - tlsDialer := &tls.Dialer{ - Config: &tls.Config{ - Certificates: []tls.Certificate{cert}, - ServerName: cfg.ServerName, - RootCAs: certPool, - }, - } - dialSocksProxy, err := proxy.SOCKS5("tcp", cfg.ProxyAddress, nil, tlsDialer) - if err != nil { - return nil, err - } - - return dialSocksProxy, nil -} - -// SecureSocksProxyEnabledOnDS checks the datasource json data to see if the secure socks proxy is enabled on it -func SecureSocksProxyEnabledOnDS(opts sdkhttpclient.Options) bool { - jsonData := backend.JSONDataFromHTTPClientOptions(opts) - res, enabled := jsonData["enableSecureSocksProxy"] - if !enabled { - return false - } - - if val, ok := res.(bool); ok { - return val - } - - return false -} diff --git a/pkg/infra/proxy/secure_socks_proxy_test.go b/pkg/infra/proxy/secure_socks_proxy_test.go deleted file mode 100644 index ca5e0973214..00000000000 --- a/pkg/infra/proxy/secure_socks_proxy_test.go +++ /dev/null @@ -1,91 +0,0 @@ -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\": 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)) - } - }) -} diff --git a/pkg/setting/setting_secure_socks_proxy.go b/pkg/setting/setting_secure_socks_proxy.go index 41689d4ef02..64700ec2199 100644 --- a/pkg/setting/setting_secure_socks_proxy.go +++ b/pkg/setting/setting_secure_socks_proxy.go @@ -3,6 +3,7 @@ package setting import ( "errors" + sdkproxy "github.com/grafana/grafana-plugin-sdk-go/backend/proxy" "gopkg.in/ini.v1" ) @@ -42,5 +43,21 @@ func readSecureSocksDSProxySettings(iniFile *ini.File) (SecureSocksDSProxySettin return s, errors.New("proxy address required") } + setDefaultProxyCli(s) + return s, nil } + +// setDefaultProxyCli overrides the default proxy cli for the sdk +// +// Note: Not optimal changing global state, but hard to not do in this case. +func setDefaultProxyCli(cfg SecureSocksDSProxySettings) { + sdkproxy.Cli = sdkproxy.NewWithCfg(&sdkproxy.ClientCfg{ + Enabled: cfg.Enabled, + ClientCert: cfg.ClientCert, + ClientKey: cfg.ClientKey, + ServerName: cfg.ServerName, + RootCA: cfg.RootCA, + ProxyAddress: cfg.ProxyAddress, + }) +} diff --git a/pkg/tsdb/mssql/mssql.go b/pkg/tsdb/mssql/mssql.go index 86002b004fa..064a5de4aff 100644 --- a/pkg/tsdb/mssql/mssql.go +++ b/pkg/tsdb/mssql/mssql.go @@ -14,12 +14,14 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" + sdkproxy "github.com/grafana/grafana-plugin-sdk-go/backend/proxy" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana-plugin-sdk-go/data/sqlutil" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb/sqleng" + "github.com/grafana/grafana/pkg/tsdb/sqleng/proxyutil" "github.com/grafana/grafana/pkg/util" ) @@ -94,8 +96,9 @@ func newInstanceSettings(cfg *setting.Cfg) datasource.InstanceFactoryFunc { driverName := "mssql" // register a new proxy driver if the secure socks proxy is enabled - if cfg.SecureSocksDSProxy.Enabled && jsonData.SecureDSProxy { - driverName, err = createMSSQLProxyDriver(&cfg.SecureSocksDSProxy, cnnstr) + proxyOpts := proxyutil.GetSQLProxyOptions(dsInfo) + if sdkproxy.Cli.SecureSocksProxyEnabled(proxyOpts) { + driverName, err = createMSSQLProxyDriver(cnnstr, proxyOpts) if err != nil { return nil, err } diff --git a/pkg/tsdb/mssql/proxy.go b/pkg/tsdb/mssql/proxy.go index 0260ac4339b..831059452ee 100644 --- a/pkg/tsdb/mssql/proxy.go +++ b/pkg/tsdb/mssql/proxy.go @@ -7,8 +7,7 @@ import ( "errors" mssql "github.com/grafana/go-mssqldb" - iproxy "github.com/grafana/grafana/pkg/infra/proxy" - "github.com/grafana/grafana/pkg/setting" + sdkproxy "github.com/grafana/grafana-plugin-sdk-go/backend/proxy" "github.com/grafana/grafana/pkg/tsdb/sqleng" "github.com/grafana/grafana/pkg/util" "golang.org/x/net/proxy" @@ -17,7 +16,7 @@ import ( // 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) { +func createMSSQLProxyDriver(cnnstr string, opts *sdkproxy.Options) (string, error) { sqleng.XormDriverMu.Lock() defer sqleng.XormDriverMu.Unlock() @@ -35,7 +34,7 @@ func createMSSQLProxyDriver(settings *setting.SecureSocksDSProxySettings, cnnstr return "", err } - driver, err := newMSSQLProxyDriver(settings, connector) + driver, err := newMSSQLProxyDriver(connector, opts) if err != nil { return "", err } @@ -57,8 +56,8 @@ 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) +func newMSSQLProxyDriver(connector *mssql.Connector, opts *sdkproxy.Options) (*mssqlProxyDriver, error) { + dialer, err := sdkproxy.Cli.NewSecureSocksProxyContextDialer(opts) if err != nil { return nil, err } diff --git a/pkg/tsdb/mssql/proxy_test.go b/pkg/tsdb/mssql/proxy_test.go index 87a3379dd07..de5bf9c8a05 100644 --- a/pkg/tsdb/mssql/proxy_test.go +++ b/pkg/tsdb/mssql/proxy_test.go @@ -6,7 +6,8 @@ import ( "testing" mssql "github.com/grafana/go-mssqldb" - "github.com/grafana/grafana/pkg/infra/proxy/proxyutil" + "github.com/grafana/grafana/pkg/tsdb/sqleng" + "github.com/grafana/grafana/pkg/tsdb/sqleng/proxyutil" "github.com/stretchr/testify/require" "xorm.io/core" ) @@ -14,18 +15,19 @@ import ( func TestMSSQLProxyDriver(t *testing.T) { settings := proxyutil.SetupTestSecureSocksProxySettings(t) dialect := "mssql" + opts := proxyutil.GetSQLProxyOptions(sqleng.DataSourceInfo{UID: "1", JsonData: sqleng.JsonData{SecureDSProxy: true}}) cnnstr := "server=127.0.0.1;port=1433;user id=sa;password=yourStrong(!)Password;database=db" - driverName, err := createMSSQLProxyDriver(settings, cnnstr) + driverName, err := createMSSQLProxyDriver(cnnstr, opts) require.NoError(t, err) t.Run("Driver should not be registered more than once", func(t *testing.T) { - testDriver, err := createMSSQLProxyDriver(settings, cnnstr) + testDriver, err := createMSSQLProxyDriver(cnnstr, opts) 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") + testDriver, err := createMSSQLProxyDriver("server=localhost;user id=sa;password=yourStrong(!)Password;database=db2", opts) require.NoError(t, err) require.NotEqual(t, driverName, testDriver) }) @@ -44,7 +46,7 @@ func TestMSSQLProxyDriver(t *testing.T) { 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) + driver, err := newMSSQLProxyDriver(connector, opts) require.NoError(t, err) conn, err := driver.OpenConnector(cnnstr) @@ -57,7 +59,7 @@ func TestMSSQLProxyDriver(t *testing.T) { 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) + driver, err := newMSSQLProxyDriver(connector, opts) require.NoError(t, err) _, err = driver.Open(cnnstr) diff --git a/pkg/tsdb/mysql/mysql.go b/pkg/tsdb/mysql/mysql.go index 141a9fedb38..59e62a6ce3d 100644 --- a/pkg/tsdb/mysql/mysql.go +++ b/pkg/tsdb/mysql/mysql.go @@ -16,6 +16,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" + sdkproxy "github.com/grafana/grafana-plugin-sdk-go/backend/proxy" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana-plugin-sdk-go/data/sqlutil" @@ -23,6 +24,7 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb/sqleng" + "github.com/grafana/grafana/pkg/tsdb/sqleng/proxyutil" ) const ( @@ -85,10 +87,11 @@ func newInstanceSettings(cfg *setting.Cfg, httpClientProvider httpclient.Provide } // register the secure socks proxy dialer context, if enabled - if cfg.SecureSocksDSProxy.Enabled && jsonData.SecureDSProxy { + proxyOpts := proxyutil.GetSQLProxyOptions(dsInfo) + if sdkproxy.Cli.SecureSocksProxyEnabled(proxyOpts) { // UID is only unique per org, the only way to ensure uniqueness is to do it by connection information uniqueIdentifier := dsInfo.User + dsInfo.DecryptedSecureJSONData["password"] + dsInfo.URL + dsInfo.Database - protocol, err = registerProxyDialerContext(&cfg.SecureSocksDSProxy, protocol, uniqueIdentifier) + protocol, err = registerProxyDialerContext(protocol, uniqueIdentifier, proxyOpts) if err != nil { return nil, err } diff --git a/pkg/tsdb/mysql/proxy.go b/pkg/tsdb/mysql/proxy.go index a7890b6fc4b..3ca495ef6a7 100644 --- a/pkg/tsdb/mysql/proxy.go +++ b/pkg/tsdb/mysql/proxy.go @@ -5,17 +5,16 @@ import ( "net" "github.com/go-sql-driver/mysql" - iproxy "github.com/grafana/grafana/pkg/infra/proxy" - "github.com/grafana/grafana/pkg/setting" + sdkproxy "github.com/grafana/grafana-plugin-sdk-go/backend/proxy" "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) { +func registerProxyDialerContext(protocol, cnnstr string, opts *sdkproxy.Options) (string, error) { // the dialer contains the true network used behind the scenes - dialer, err := getProxyDialerContext(settings, protocol) + dialer, err := getProxyDialerContext(protocol, opts) if err != nil { return "", err } @@ -39,8 +38,8 @@ type mySQLContextDialer struct { } // 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) +func getProxyDialerContext(actualNetwork string, opts *sdkproxy.Options) (*mySQLContextDialer, error) { + dialer, err := sdkproxy.Cli.NewSecureSocksProxyContextDialer(opts) if err != nil { return nil, err } diff --git a/pkg/tsdb/mysql/proxy_test.go b/pkg/tsdb/mysql/proxy_test.go index 429768cf06d..62514716319 100644 --- a/pkg/tsdb/mysql/proxy_test.go +++ b/pkg/tsdb/mysql/proxy_test.go @@ -6,7 +6,8 @@ import ( "testing" "github.com/go-sql-driver/mysql" - "github.com/grafana/grafana/pkg/infra/proxy/proxyutil" + "github.com/grafana/grafana/pkg/tsdb/sqleng" + "github.com/grafana/grafana/pkg/tsdb/sqleng/proxyutil" "github.com/stretchr/testify/require" ) @@ -14,10 +15,11 @@ func TestMySQLProxyDialer(t *testing.T) { settings := proxyutil.SetupTestSecureSocksProxySettings(t) protocol := "tcp" - network, err := registerProxyDialerContext(settings, protocol, "1") + opts := proxyutil.GetSQLProxyOptions(sqleng.DataSourceInfo{UID: "1", JsonData: sqleng.JsonData{SecureDSProxy: true}}) + dbURL := "localhost:5432" + network, err := registerProxyDialerContext(protocol, dbURL, opts) require.NoError(t, err) driver := mysql.MySQLDriver{} - dbURL := "localhost:5432" cnnstr := fmt.Sprintf("test:test@%s(%s)/db", network, dbURL, @@ -28,7 +30,7 @@ func TestMySQLProxyDialer(t *testing.T) { }) t.Run("Multiple networks can be created", func(t *testing.T) { - network, err := registerProxyDialerContext(settings, protocol, "2") + network, err := registerProxyDialerContext(protocol, dbURL, opts) require.NoError(t, err) cnnstr2 := fmt.Sprintf("test:test@%s(%s)/db", network, diff --git a/pkg/tsdb/postgres/postgres.go b/pkg/tsdb/postgres/postgres.go index 3b16e5fc29e..1c192adc533 100644 --- a/pkg/tsdb/postgres/postgres.go +++ b/pkg/tsdb/postgres/postgres.go @@ -11,12 +11,14 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" + sdkproxy "github.com/grafana/grafana-plugin-sdk-go/backend/proxy" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana-plugin-sdk-go/data/sqlutil" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb/sqleng" + "github.com/grafana/grafana/pkg/tsdb/sqleng/proxyutil" ) var logger = log.New("tsdb.postgres") @@ -95,8 +97,9 @@ func (s *Service) newInstanceSettings(cfg *setting.Cfg) datasource.InstanceFacto driverName := "postgres" // register a proxy driver if the secure socks proxy is enabled - if cfg.SecureSocksDSProxy.Enabled && jsonData.SecureDSProxy { - driverName, err = createPostgresProxyDriver(&cfg.SecureSocksDSProxy, cnnstr) + proxyOpts := proxyutil.GetSQLProxyOptions(dsInfo) + if sdkproxy.Cli.SecureSocksProxyEnabled(proxyOpts) { + driverName, err = createPostgresProxyDriver(cnnstr, proxyOpts) if err != nil { return "", nil } diff --git a/pkg/tsdb/postgres/proxy.go b/pkg/tsdb/postgres/proxy.go index ed5e17487cb..fbe4f43a8e9 100644 --- a/pkg/tsdb/postgres/proxy.go +++ b/pkg/tsdb/postgres/proxy.go @@ -7,8 +7,7 @@ import ( "net" "time" - iproxy "github.com/grafana/grafana/pkg/infra/proxy" - "github.com/grafana/grafana/pkg/setting" + 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" @@ -18,7 +17,7 @@ import ( // 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) { +func createPostgresProxyDriver(cnnstr string, opts *sdkproxy.Options) (string, error) { sqleng.XormDriverMu.Lock() defer sqleng.XormDriverMu.Unlock() @@ -36,7 +35,7 @@ func createPostgresProxyDriver(settings *setting.SecureSocksDSProxySettings, cnn return "", err } - driver, err := newPostgresProxyDriver(settings, connector) + driver, err := newPostgresProxyDriver(connector, opts) if err != nil { return "", err } @@ -58,8 +57,8 @@ 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) +func newPostgresProxyDriver(connector *pq.Connector, opts *sdkproxy.Options) (*postgresProxyDriver, error) { + dialer, err := sdkproxy.Cli.NewSecureSocksProxyContextDialer(opts) if err != nil { return nil, err } diff --git a/pkg/tsdb/postgres/proxy_test.go b/pkg/tsdb/postgres/proxy_test.go index 9da43f5073d..819d8cc93b6 100644 --- a/pkg/tsdb/postgres/proxy_test.go +++ b/pkg/tsdb/postgres/proxy_test.go @@ -5,7 +5,8 @@ import ( "fmt" "testing" - "github.com/grafana/grafana/pkg/infra/proxy/proxyutil" + "github.com/grafana/grafana/pkg/tsdb/sqleng" + "github.com/grafana/grafana/pkg/tsdb/sqleng/proxyutil" "github.com/lib/pq" "github.com/stretchr/testify/require" "xorm.io/core" @@ -13,20 +14,21 @@ import ( func TestPostgresProxyDriver(t *testing.T) { dialect := "postgres" + opts := proxyutil.GetSQLProxyOptions(sqleng.DataSourceInfo{UID: "1", JsonData: sqleng.JsonData{SecureDSProxy: true}}) settings := proxyutil.SetupTestSecureSocksProxySettings(t) dbURL := "localhost:5432" cnnstr := fmt.Sprintf("postgres://auser:password@%s/db?sslmode=disable", dbURL) - driverName, err := createPostgresProxyDriver(settings, cnnstr) + driverName, err := createPostgresProxyDriver(cnnstr, opts) require.NoError(t, err) t.Run("Driver should not be registered more than once", func(t *testing.T) { - testDriver, err := createPostgresProxyDriver(settings, cnnstr) + testDriver, err := createPostgresProxyDriver(cnnstr, opts) 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") + testDriver, err := createPostgresProxyDriver("server=localhost;user id=sa;password=yourStrong(!)Password;database=db2", opts) require.NoError(t, err) require.NotEqual(t, driverName, testDriver) }) @@ -45,7 +47,7 @@ func TestPostgresProxyDriver(t *testing.T) { 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) + driver, err := newPostgresProxyDriver(connector, opts) require.NoError(t, err) conn, err := driver.OpenConnector(cnnstr) @@ -58,7 +60,7 @@ func TestPostgresProxyDriver(t *testing.T) { 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) + driver, err := newPostgresProxyDriver(connector, opts) require.NoError(t, err) conn, err := driver.OpenConnector(cnnstr) diff --git a/pkg/infra/proxy/proxyutil/proxyutil.go b/pkg/tsdb/sqleng/proxyutil/proxy_test_util.go similarity index 91% rename from pkg/infra/proxy/proxyutil/proxyutil.go rename to pkg/tsdb/sqleng/proxyutil/proxy_test_util.go index 79f5e939be3..a5e31263a01 100644 --- a/pkg/infra/proxy/proxyutil/proxyutil.go +++ b/pkg/tsdb/sqleng/proxyutil/proxy_test_util.go @@ -12,11 +12,12 @@ import ( "testing" "time" - "github.com/grafana/grafana/pkg/setting" + sdkproxy "github.com/grafana/grafana-plugin-sdk-go/backend/proxy" "github.com/stretchr/testify/require" ) -func SetupTestSecureSocksProxySettings(t *testing.T) *setting.SecureSocksDSProxySettings { +func SetupTestSecureSocksProxySettings(t *testing.T) *sdkproxy.ClientCfg { + t.Helper() proxyAddress := "localhost:3000" serverName := "localhost" tempDir := t.TempDir() @@ -98,11 +99,15 @@ func SetupTestSecureSocksProxySettings(t *testing.T) *setting.SecureSocksDSProxy err = keyFile.Close() require.NoError(t, err) - return &setting.SecureSocksDSProxySettings{ + settings := &sdkproxy.ClientCfg{ + Enabled: true, ClientCert: clientCert, ClientKey: clientKey, RootCA: rootCACert, ServerName: serverName, ProxyAddress: proxyAddress, } + + sdkproxy.Cli = sdkproxy.NewWithCfg(settings) + return settings } diff --git a/pkg/tsdb/sqleng/proxyutil/proxy_util.go b/pkg/tsdb/sqleng/proxyutil/proxy_util.go new file mode 100644 index 00000000000..dd2eda548fd --- /dev/null +++ b/pkg/tsdb/sqleng/proxyutil/proxy_util.go @@ -0,0 +1,15 @@ +package proxyutil + +import ( + sdkproxy "github.com/grafana/grafana-plugin-sdk-go/backend/proxy" + "github.com/grafana/grafana/pkg/tsdb/sqleng" +) + +func GetSQLProxyOptions(dsInfo sqleng.DataSourceInfo) *sdkproxy.Options { + return &sdkproxy.Options{ + Enabled: dsInfo.JsonData.SecureDSProxy, + Auth: &sdkproxy.AuthOptions{ + Username: dsInfo.UID, + }, + } +}