Skip to content

Commit 91cb987

Browse files
Secure socks proxy: Modify how proxy options can be set (#717)
1 parent 51d836b commit 91cb987

File tree

6 files changed

+171
-46
lines changed

6 files changed

+171
-46
lines changed

backend/common.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"time"
77

88
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
9+
"github.com/grafana/grafana-plugin-sdk-go/backend/proxy"
910
"github.com/grafana/grafana-plugin-sdk-go/internal/tenant"
1011
)
1112

@@ -122,6 +123,11 @@ func (s *DataSourceInstanceSettings) HTTPClientOptions() (httpclient.Options, er
122123

123124
setCustomOptionsFromHTTPSettings(&opts, httpSettings)
124125

126+
opts.ProxyOptions, err = s.ProxyOptions()
127+
if err != nil {
128+
return opts, err
129+
}
130+
125131
return opts, nil
126132
}
127133

@@ -214,3 +220,50 @@ func propagateTenantIDIfPresent(ctx context.Context) context.Context {
214220
}
215221
return ctx
216222
}
223+
224+
func (s *DataSourceInstanceSettings) ProxyOptions() (*proxy.Options, error) {
225+
opts := &proxy.Options{}
226+
227+
var dat map[string]interface{}
228+
if s.JSONData != nil {
229+
if err := json.Unmarshal(s.JSONData, &dat); err != nil {
230+
return nil, err
231+
}
232+
}
233+
234+
opts.Enabled = proxy.SecureSocksProxyEnabledOnDS(dat)
235+
if !opts.Enabled {
236+
return nil, nil
237+
}
238+
239+
opts.Auth = &proxy.AuthOptions{}
240+
opts.Timeouts = &proxy.TimeoutOptions{}
241+
if v, exists := dat["secureSocksProxyUsername"]; exists {
242+
opts.Auth.Username = v.(string)
243+
} else {
244+
// default username is the datasource uid
245+
opts.Auth.Username = s.UID
246+
}
247+
248+
if v, exists := s.DecryptedSecureJSONData["secureSocksProxyPassword"]; exists {
249+
opts.Auth.Password = v
250+
}
251+
252+
if v, exists := dat["timeout"]; exists {
253+
if iv, ok := v.(float64); ok {
254+
opts.Timeouts.Timeout = time.Duration(iv) * time.Second
255+
}
256+
} else {
257+
opts.Timeouts.Timeout = proxy.DefaultTimeoutOptions.Timeout
258+
}
259+
260+
if v, exists := dat["keepAlive"]; exists {
261+
if iv, ok := v.(float64); ok {
262+
opts.Timeouts.KeepAlive = time.Duration(iv) * time.Second
263+
}
264+
} else {
265+
opts.Timeouts.KeepAlive = proxy.DefaultTimeoutOptions.KeepAlive
266+
}
267+
268+
return opts, nil
269+
}

backend/common_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package backend
22

33
import (
44
"testing"
5+
"time"
56

67
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
8+
"github.com/grafana/grafana-plugin-sdk-go/backend/proxy"
79
"github.com/stretchr/testify/assert"
810
"github.com/stretchr/testify/require"
911
)
@@ -163,6 +165,27 @@ func TestDataSourceInstanceSettings(t *testing.T) {
163165
},
164166
},
165167
},
168+
{
169+
instanceSettings: &DataSourceInstanceSettings{
170+
UID: "uid1",
171+
JSONData: []byte("{ \"enableSecureSocksProxy\": true }"),
172+
DecryptedSecureJSONData: map[string]string{},
173+
},
174+
expectedClientOptions: httpclient.Options{
175+
ProxyOptions: &proxy.Options{
176+
Enabled: true,
177+
Auth: &proxy.AuthOptions{
178+
Username: "uid1",
179+
},
180+
},
181+
CustomOptions: map[string]interface{}{
182+
dataCustomOptionsKey: map[string]interface{}{
183+
"enableSecureSocksProxy": true,
184+
},
185+
secureDataCustomOptionsKey: map[string]string{},
186+
},
187+
},
188+
},
166189
}
167190

168191
for _, tc := range tcs {
@@ -237,3 +260,96 @@ func TestCustomOptions(t *testing.T) {
237260
require.Empty(t, secureJSONData)
238261
})
239262
}
263+
264+
func TestProxyOptions(t *testing.T) {
265+
t.Run("ProxyOptions() should translate settings as expected", func(t *testing.T) {
266+
tcs := []struct {
267+
instanceSettings *DataSourceInstanceSettings
268+
expectedClientOptions *proxy.Options
269+
}{
270+
{
271+
instanceSettings: &DataSourceInstanceSettings{},
272+
expectedClientOptions: nil,
273+
},
274+
{
275+
instanceSettings: &DataSourceInstanceSettings{
276+
Name: "ds1",
277+
UID: "uid1",
278+
User: "user",
279+
Type: "example-datasource",
280+
JSONData: []byte("{ \"enableSecureSocksProxy\": false }"),
281+
BasicAuthEnabled: true,
282+
BasicAuthUser: "buser",
283+
},
284+
expectedClientOptions: nil,
285+
},
286+
{
287+
instanceSettings: &DataSourceInstanceSettings{
288+
Name: "ds1",
289+
UID: "uid1",
290+
User: "user",
291+
Type: "example-datasource",
292+
JSONData: []byte("{ \"enableSecureSocksProxy\": true }"),
293+
BasicAuthEnabled: true,
294+
BasicAuthUser: "buser",
295+
},
296+
expectedClientOptions: &proxy.Options{
297+
Enabled: true,
298+
Auth: &proxy.AuthOptions{
299+
Username: "uid1",
300+
},
301+
Timeouts: &proxy.DefaultTimeoutOptions,
302+
},
303+
},
304+
{
305+
instanceSettings: &DataSourceInstanceSettings{
306+
Name: "ds1",
307+
UID: "uid1",
308+
User: "user",
309+
Type: "example-datasource",
310+
JSONData: []byte("{ \"enableSecureSocksProxy\": true, \"secureSocksProxyUsername\": \"username\" }"),
311+
BasicAuthEnabled: true,
312+
BasicAuthUser: "buser",
313+
DecryptedSecureJSONData: map[string]string{
314+
"secureSocksProxyPassword": "pswd",
315+
},
316+
},
317+
expectedClientOptions: &proxy.Options{
318+
Enabled: true,
319+
Auth: &proxy.AuthOptions{
320+
Username: "username",
321+
Password: "pswd",
322+
},
323+
Timeouts: &proxy.DefaultTimeoutOptions,
324+
},
325+
},
326+
{
327+
instanceSettings: &DataSourceInstanceSettings{
328+
Name: "ds1",
329+
UID: "uid1",
330+
User: "user",
331+
Type: "example-datasource",
332+
JSONData: []byte("{ \"enableSecureSocksProxy\": true, \"timeout\": 10, \"keepAlive\": 15 }"),
333+
BasicAuthEnabled: true,
334+
BasicAuthUser: "buser",
335+
},
336+
expectedClientOptions: &proxy.Options{
337+
Enabled: true,
338+
Auth: &proxy.AuthOptions{
339+
Username: "uid1",
340+
},
341+
Timeouts: &proxy.TimeoutOptions{
342+
KeepAlive: time.Second * 15,
343+
Timeout: time.Second * 10,
344+
},
345+
},
346+
},
347+
}
348+
349+
for _, tc := range tcs {
350+
opts, err := tc.instanceSettings.ProxyOptions()
351+
assert.NoError(t, err)
352+
assert.Equal(t, tc.expectedClientOptions, opts)
353+
}
354+
})
355+
}

backend/http_settings.go

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"time"
77

88
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
9-
"github.com/grafana/grafana-plugin-sdk-go/backend/proxy"
109
)
1110

1211
// HTTPSettings is a convenient struct for holding decoded HTTP settings from
@@ -46,10 +45,6 @@ type HTTPSettings struct {
4645
SigV4AccessKey string
4746
SigV4SecretKey string
4847

49-
SecureSocksProxyEnabled bool
50-
SecureSocksProxyUsername string
51-
SecureSocksProxyPass string
52-
5348
JSONData map[string]interface{}
5449
SecureJSONData map[string]string
5550
}
@@ -103,20 +98,6 @@ func (s *HTTPSettings) HTTPClientOptions() httpclient.Options {
10398
}
10499
}
105100

106-
if s.SecureSocksProxyEnabled {
107-
opts.ProxyOptions = &proxy.Options{
108-
Enabled: s.SecureSocksProxyEnabled,
109-
Timeouts: &proxy.TimeoutOptions{
110-
Timeout: s.Timeout,
111-
KeepAlive: s.KeepAlive,
112-
},
113-
Auth: &proxy.AuthOptions{
114-
Username: s.SecureSocksProxyUsername,
115-
Password: s.SecureSocksProxyPass,
116-
},
117-
}
118-
}
119-
120101
return opts
121102
}
122103

@@ -284,20 +265,6 @@ func parseHTTPSettings(jsonData json.RawMessage, secureJSONData map[string]strin
284265
}
285266
}
286267

287-
// secure socks proxy
288-
if v, exists := dat["enableSecureSocksProxy"]; exists {
289-
s.SecureSocksProxyEnabled = v.(bool)
290-
}
291-
292-
if s.SecureSocksProxyEnabled {
293-
if v, exists := dat["secureSocksProxyUsername"]; exists {
294-
s.SecureSocksProxyUsername = v.(string)
295-
}
296-
if v, exists := secureJSONData["secureSocksProxyPassword"]; exists {
297-
s.SecureSocksProxyPass = v
298-
}
299-
}
300-
301268
// headers
302269
index := 1
303270
for {

backend/httpclient/http_client.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -159,17 +159,6 @@ func createOptions(providedOpts ...Options) Options {
159159
opts.Middlewares = DefaultMiddlewares()
160160
}
161161

162-
if proxy.SecureSocksProxyEnabled(opts.ProxyOptions) {
163-
// default username is the datasource uid, this can be updated
164-
// by setting `secureSocksProxyUsername` in the datasource json
165-
if opts.ProxyOptions.Auth == nil {
166-
opts.ProxyOptions.Auth = &proxy.AuthOptions{}
167-
}
168-
if opts.ProxyOptions.Auth.Username == "" {
169-
opts.ProxyOptions.Auth.Username = opts.Labels["datasource_uid"]
170-
}
171-
}
172-
173162
return opts
174163
}
175164

backend/proxy/options.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var DefaultTimeoutOptions = TimeoutOptions{
2828
KeepAlive: 30 * time.Second,
2929
}
3030

31-
func createOptions(providedOpts *Options) Options {
31+
func setDefaults(providedOpts *Options) Options {
3232
var opts Options
3333
if providedOpts == nil {
3434
return opts

backend/proxy/secure_socks_proxy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func NewSecureSocksProxyContextDialer(opts *Options) (proxy.Dialer, error) {
114114
return nil, err
115115
}
116116

117-
clientOpts := createOptions(opts)
117+
clientOpts := setDefaults(opts)
118118

119119
certPool := x509.NewCertPool()
120120
for _, rootCAFile := range strings.Split(cfg.rootCA, " ") {

0 commit comments

Comments
 (0)