Skip to content

Commit 30591d3

Browse files
Secure socks proxy: Allow the env variables to be overriden (#716)
1 parent 91cb987 commit 30591d3

File tree

5 files changed

+175
-139
lines changed

5 files changed

+175
-139
lines changed

backend/httpclient/http_client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func GetTransport(opts ...Options) (http.RoundTripper, error) {
8686
clientOpts.Middlewares = clientOpts.ConfigureMiddleware(clientOpts, clientOpts.Middlewares)
8787
}
8888

89-
err = proxy.ConfigureSecureSocksHTTPProxy(transport, clientOpts.ProxyOptions)
89+
err = proxy.Cli.ConfigureSecureSocksHTTPProxy(transport, clientOpts.ProxyOptions)
9090
if err != nil {
9191
return nil, err
9292
}

backend/proxy/secure_socks_proxy.go

Lines changed: 83 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
)
1717

1818
var (
19-
2019
// PluginSecureSocksProxyEnabled is a constant for the GF_SECURE_SOCKS_DATASOURCE_PROXY_SERVER_ENABLED
2120
// environment variable used to specify if a secure socks proxy is allowed to be used for datasource connections.
2221
PluginSecureSocksProxyEnabled = "GF_SECURE_SOCKS_DATASOURCE_PROXY_SERVER_ENABLED"
@@ -37,62 +36,63 @@ var (
3736
PluginSecureSocksProxyServerName = "GF_SECURE_SOCKS_DATASOURCE_PROXY_SERVER_NAME"
3837
)
3938

40-
// SecureSocksProxyConfig contains the information needed to allow datasource connections to be
41-
// proxied to a secure socks proxy
42-
type secureSocksProxyConfig struct {
43-
clientCert string
44-
clientKey string
45-
rootCA string
46-
proxyAddress string
47-
serverName string
39+
// Client is the main Proxy Client interface.
40+
type Client interface {
41+
SecureSocksProxyEnabled(opts *Options) bool
42+
ConfigureSecureSocksHTTPProxy(transport *http.Transport, opts *Options) error
43+
NewSecureSocksProxyContextDialer(opts *Options) (proxy.Dialer, error)
4844
}
4945

50-
// SecureSocksProxyEnabled checks if the Grafana instance allows the secure socks proxy to be used
51-
// and the datasource options specify to use the proxy
52-
func SecureSocksProxyEnabled(opts *Options) bool {
53-
if opts == nil {
54-
return false
55-
}
46+
// ClientCfg contains the information needed to allow datasource connections to be
47+
// proxied to a secure socks proxy.
48+
type ClientCfg struct {
49+
Enabled bool
50+
ClientCert string
51+
ClientKey string
52+
RootCA string
53+
ProxyAddress string
54+
ServerName string
55+
}
5656

57-
if !opts.Enabled {
58-
return false
59-
}
57+
// Cli is the default Proxy Client.
58+
var Cli = New()
6059

61-
if value, ok := os.LookupEnv(PluginSecureSocksProxyEnabled); ok {
62-
enabledOnInst, err := strconv.ParseBool(value)
63-
if err != nil {
64-
return false
65-
}
60+
// New creates a new proxy client from the environment variables set by the grafana-server in the plugin.
61+
func New() Client {
62+
return NewWithCfg(getConfigFromEnv())
63+
}
6664

67-
return enabledOnInst
65+
// NewWithCfg creates a new proxy client from a given config.
66+
func NewWithCfg(cfg *ClientCfg) Client {
67+
return &cfgProxyWrapper{
68+
cfg: cfg,
6869
}
70+
}
6971

70-
return false
72+
type cfgProxyWrapper struct {
73+
cfg *ClientCfg
7174
}
7275

73-
// SecureSocksProxyEnabledOnDS checks the datasource json data for `enableSecureSocksProxy`
74-
// to determine if the secure socks proxy should be enabled on it
75-
func SecureSocksProxyEnabledOnDS(jsonData map[string]interface{}) bool {
76-
res, enabled := jsonData["enableSecureSocksProxy"]
77-
if !enabled {
76+
// SecureSocksProxyEnabled checks if the Grafana instance allows the secure socks proxy to be used
77+
// and the datasource options specify to use the proxy
78+
func (p *cfgProxyWrapper) SecureSocksProxyEnabled(opts *Options) bool {
79+
// it cannot be enabled if it's not enabled on Grafana
80+
if p.cfg == nil || !p.cfg.Enabled {
7881
return false
7982
}
8083

81-
if val, ok := res.(bool); ok {
82-
return val
83-
}
84-
85-
return false
84+
// if it's enabled on Grafana, check if the datasource is using it
85+
return (opts != nil) && opts.Enabled
8686
}
8787

8888
// ConfigureSecureSocksHTTPProxy takes a http.DefaultTransport and wraps it in a socks5 proxy with TLS
8989
// if it is enabled on the datasource and the grafana instance
90-
func ConfigureSecureSocksHTTPProxy(transport *http.Transport, opts *Options) error {
91-
if !SecureSocksProxyEnabled(opts) {
90+
func (p *cfgProxyWrapper) ConfigureSecureSocksHTTPProxy(transport *http.Transport, opts *Options) error {
91+
if !p.SecureSocksProxyEnabled(opts) {
9292
return nil
9393
}
9494

95-
dialSocksProxy, err := NewSecureSocksProxyContextDialer(opts)
95+
dialSocksProxy, err := p.NewSecureSocksProxyContextDialer(opts)
9696
if err != nil {
9797
return err
9898
}
@@ -107,17 +107,15 @@ func ConfigureSecureSocksHTTPProxy(transport *http.Transport, opts *Options) err
107107
}
108108

109109
// NewSecureSocksProxyContextDialer returns a proxy context dialer that can be used to allow datasource connections to go through a secure socks proxy
110-
func NewSecureSocksProxyContextDialer(opts *Options) (proxy.Dialer, error) {
111-
var err error
112-
cfg, err := getConfigFromEnv()
113-
if err != nil {
114-
return nil, err
110+
func (p *cfgProxyWrapper) NewSecureSocksProxyContextDialer(opts *Options) (proxy.Dialer, error) {
111+
if !p.SecureSocksProxyEnabled(opts) {
112+
return nil, fmt.Errorf("proxy not enabled")
115113
}
116114

117115
clientOpts := setDefaults(opts)
118116

119117
certPool := x509.NewCertPool()
120-
for _, rootCAFile := range strings.Split(cfg.rootCA, " ") {
118+
for _, rootCAFile := range strings.Split(p.cfg.RootCA, " ") {
121119
// nolint:gosec
122120
// The gosec G304 warning can be ignored because `rootCAFile` comes from config ini
123121
// and we check below if it's the right file type
@@ -136,15 +134,15 @@ func NewSecureSocksProxyContextDialer(opts *Options) (proxy.Dialer, error) {
136134
}
137135
}
138136

139-
cert, err := tls.LoadX509KeyPair(cfg.clientCert, cfg.clientKey)
137+
cert, err := tls.LoadX509KeyPair(p.cfg.ClientCert, p.cfg.ClientKey)
140138
if err != nil {
141139
return nil, err
142140
}
143141

144142
tlsDialer := &tls.Dialer{
145143
Config: &tls.Config{
146144
Certificates: []tls.Certificate{cert},
147-
ServerName: cfg.serverName,
145+
ServerName: p.cfg.ServerName,
148146
RootCAs: certPool,
149147
MinVersion: tls.VersionTLS13,
150148
},
@@ -154,15 +152,15 @@ func NewSecureSocksProxyContextDialer(opts *Options) (proxy.Dialer, error) {
154152
},
155153
}
156154

157-
var dsInfo *proxy.Auth
155+
var auth *proxy.Auth
158156
if clientOpts.Auth != nil {
159-
dsInfo = &proxy.Auth{
157+
auth = &proxy.Auth{
160158
User: clientOpts.Auth.Username,
161159
Password: clientOpts.Auth.Password,
162160
}
163161
}
164162

165-
dialSocksProxy, err := proxy.SOCKS5("tcp", cfg.proxyAddress, dsInfo, tlsDialer)
163+
dialSocksProxy, err := proxy.SOCKS5("tcp", p.cfg.ProxyAddress, auth, tlsDialer)
166164
if err != nil {
167165
return nil, err
168166
}
@@ -171,47 +169,70 @@ func NewSecureSocksProxyContextDialer(opts *Options) (proxy.Dialer, error) {
171169
}
172170

173171
// getConfigFromEnv gets the needed proxy information from the env variables that Grafana set with the values from the config ini
174-
func getConfigFromEnv() (*secureSocksProxyConfig, error) {
172+
func getConfigFromEnv() *ClientCfg {
173+
if value, ok := os.LookupEnv(PluginSecureSocksProxyEnabled); ok {
174+
enabled, err := strconv.ParseBool(value)
175+
if err != nil || !enabled {
176+
return nil
177+
}
178+
}
179+
175180
clientCert := ""
176181
if value, ok := os.LookupEnv(PluginSecureSocksProxyClientCert); ok {
177182
clientCert = value
178183
} else {
179-
return nil, fmt.Errorf("missing client cert")
184+
return nil
180185
}
181186

182187
clientKey := ""
183188
if value, ok := os.LookupEnv(PluginSecureSocksProxyClientKey); ok {
184189
clientKey = value
185190
} else {
186-
return nil, fmt.Errorf("missing client key")
191+
return nil
187192
}
188193

189194
rootCA := ""
190195
if value, ok := os.LookupEnv(PluginSecureSocksProxyRootCACert); ok {
191196
rootCA = value
192197
} else {
193-
return nil, fmt.Errorf("missing root ca")
198+
return nil
194199
}
195200

196201
proxyAddress := ""
197202
if value, ok := os.LookupEnv(PluginSecureSocksProxyProxyAddress); ok {
198203
proxyAddress = value
199204
} else {
200-
return nil, fmt.Errorf("missing proxy address")
205+
return nil
201206
}
202207

203208
serverName := ""
204209
if value, ok := os.LookupEnv(PluginSecureSocksProxyServerName); ok {
205210
serverName = value
206211
} else {
207-
return nil, fmt.Errorf("missing server name")
212+
return nil
213+
}
214+
215+
return &ClientCfg{
216+
Enabled: true,
217+
ClientCert: clientCert,
218+
ClientKey: clientKey,
219+
RootCA: rootCA,
220+
ProxyAddress: proxyAddress,
221+
ServerName: serverName,
222+
}
223+
}
224+
225+
// SecureSocksProxyEnabledOnDS checks the datasource json data for `enableSecureSocksProxy`
226+
// to determine if the secure socks proxy should be enabled on it
227+
func SecureSocksProxyEnabledOnDS(jsonData map[string]interface{}) bool {
228+
res, enabled := jsonData["enableSecureSocksProxy"]
229+
if !enabled {
230+
return false
231+
}
232+
233+
if val, ok := res.(bool); ok {
234+
return val
208235
}
209236

210-
return &secureSocksProxyConfig{
211-
clientCert: clientCert,
212-
clientKey: clientKey,
213-
rootCA: rootCA,
214-
proxyAddress: proxyAddress,
215-
serverName: serverName,
216-
}, nil
237+
return false
217238
}

0 commit comments

Comments
 (0)