Skip to content

Commit

Permalink
Support PDC file contents instead of file paths (#933)
Browse files Browse the repository at this point in the history
* read PDC info instead of using file paths

* add request specific vars for pdc

* update constant vars

* undo some naming changes

* fix new cfg names

* move tests

* remove gosec nolint

* separate on comma

* apply PR feedback

* add check for client config
  • Loading branch information
wbrowne authored Mar 22, 2024
1 parent 3f1f4c5 commit f88fae5
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 278 deletions.
4 changes: 2 additions & 2 deletions backend/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ func TestProxyOptions(t *testing.T) {
proxyClientCfg: &proxy.ClientCfg{
ClientCert: "<client-cert>",
ClientKey: "123abc",
RootCA: "<root-ca-cert>",
RootCAs: []string{"<root-ca-cert>"},
ProxyAddress: "10.1.2.3",
ServerName: "grafana-server",
},
Expand All @@ -381,7 +381,7 @@ func TestProxyOptions(t *testing.T) {
ClientCfg: &proxy.ClientCfg{
ClientCert: "<client-cert>",
ClientKey: "123abc",
RootCA: "<root-ca-cert>",
RootCAs: []string{"<root-ca-cert>"},
ProxyAddress: "10.1.2.3",
ServerName: "grafana-server",
},
Expand Down
2 changes: 1 addition & 1 deletion backend/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (c *GrafanaCfg) proxy() (Proxy, error) {
clientCfg: &proxy.ClientCfg{
ClientCert: c.Get(proxy.PluginSecureSocksProxyClientCert),
ClientKey: c.Get(proxy.PluginSecureSocksProxyClientKey),
RootCA: c.Get(proxy.PluginSecureSocksProxyRootCACert),
RootCAs: strings.Split(c.Get(proxy.PluginSecureSocksProxyRootCAs), ","),
ProxyAddress: c.Get(proxy.PluginSecureSocksProxyProxyAddress),
ServerName: c.Get(proxy.PluginSecureSocksProxyServerName),
AllowInsecure: allowInsecure,
Expand Down
14 changes: 7 additions & 7 deletions backend/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestConfig(t *testing.T) {
proxy.PluginSecureSocksProxyServerName: "localhost",
proxy.PluginSecureSocksProxyClientKey: "clientKey",
proxy.PluginSecureSocksProxyClientCert: "clientCert",
proxy.PluginSecureSocksProxyRootCACert: "rootCACert",
proxy.PluginSecureSocksProxyRootCAs: "rootCACert",
}),
expectedFeatureToggles: FeatureToggles{
enabled: map[string]struct{}{
Expand All @@ -64,7 +64,7 @@ func TestConfig(t *testing.T) {
clientCfg: &proxy.ClientCfg{
ClientCert: "clientCert",
ClientKey: "clientKey",
RootCA: "rootCACert",
RootCAs: []string{"rootCACert"},
ProxyAddress: "localhost:1234",
ServerName: "localhost",
},
Expand All @@ -79,7 +79,7 @@ func TestConfig(t *testing.T) {
proxy.PluginSecureSocksProxyServerName: "localhost",
proxy.PluginSecureSocksProxyClientKey: "clientKey",
proxy.PluginSecureSocksProxyClientCert: "clientCert",
proxy.PluginSecureSocksProxyRootCACert: "rootCACert",
proxy.PluginSecureSocksProxyRootCAs: "rootCACert",
}),
expectedFeatureToggles: FeatureToggles{
enabled: map[string]struct{}{
Expand All @@ -97,14 +97,14 @@ func TestConfig(t *testing.T) {
proxy.PluginSecureSocksProxyServerName: "localhost",
proxy.PluginSecureSocksProxyClientKey: "clientKey",
proxy.PluginSecureSocksProxyClientCert: "clientCert",
proxy.PluginSecureSocksProxyRootCACert: "rootCACert",
proxy.PluginSecureSocksProxyRootCAs: "rootCACert",
}),
expectedFeatureToggles: FeatureToggles{},
expectedProxy: Proxy{
clientCfg: &proxy.ClientCfg{
ClientCert: "clientCert",
ClientKey: "clientKey",
RootCA: "rootCACert",
RootCAs: []string{"rootCACert"},
ProxyAddress: "localhost:1234",
ServerName: "localhost",
},
Expand All @@ -119,15 +119,15 @@ func TestConfig(t *testing.T) {
proxy.PluginSecureSocksProxyServerName: "localhost",
proxy.PluginSecureSocksProxyClientKey: "clientKey",
proxy.PluginSecureSocksProxyClientCert: "clientCert",
proxy.PluginSecureSocksProxyRootCACert: "rootCACert",
proxy.PluginSecureSocksProxyRootCAs: "rootCACert",
proxy.PluginSecureSocksProxyAllowInsecure: "true",
}),
expectedFeatureToggles: FeatureToggles{},
expectedProxy: Proxy{
clientCfg: &proxy.ClientCfg{
ClientCert: "clientCert",
ClientKey: "clientKey",
RootCA: "rootCACert",
RootCAs: []string{"rootCACert"},
ProxyAddress: "localhost:1234",
ServerName: "localhost",
AllowInsecure: true,
Expand Down
112 changes: 112 additions & 0 deletions backend/proxy/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package proxy

import (
"os"
"strconv"
"strings"
)

const (
// Deprecated: PluginSecureSocksProxyEnabledEnvVarName is a constant for the GF_SECURE_SOCKS_DATASOURCE_PROXY_SERVER_ENABLED
// environment variable used to specify if a secure socks proxy is allowed to be used for datasource connections.
PluginSecureSocksProxyEnabledEnvVarName = "GF_SECURE_SOCKS_DATASOURCE_PROXY_SERVER_ENABLED"
// Deprecated: PluginSecureSocksProxyClientCertFilePathEnvVarName is a constant for the GF_SECURE_SOCKS_DATASOURCE_PROXY_CLIENT_CERT
// environment variable used to specify the file location of the client cert for the secure socks proxy.
PluginSecureSocksProxyClientCertFilePathEnvVarName = "GF_SECURE_SOCKS_DATASOURCE_PROXY_CLIENT_CERT"
// Deprecated: PluginSecureSocksProxyClientKeyFilePathEnvVarName is a constant for the GF_SECURE_SOCKS_DATASOURCE_PROXY_CLIENT_KEY
// environment variable used to specify the file location of the client key for the secure socks proxy.
PluginSecureSocksProxyClientKeyFilePathEnvVarName = "GF_SECURE_SOCKS_DATASOURCE_PROXY_CLIENT_KEY"
// Deprecated: PluginSecureSocksProxyRootCACertFilePathsEnvVarName is a constant for the GF_SECURE_SOCKS_DATASOURCE_PROXY_ROOT_CA_CERT
// environment variable used to specify the file location of the root ca for the secure socks proxy.
PluginSecureSocksProxyRootCACertFilePathsEnvVarName = "GF_SECURE_SOCKS_DATASOURCE_PROXY_ROOT_CA_CERT"
// Deprecated: PluginSecureSocksProxyAddressEnvVarName is a constant for the GF_SECURE_SOCKS_DATASOURCE_PROXY_PROXY_ADDRESS
// environment variable used to specify the secure socks proxy server address to proxy the connections to.
PluginSecureSocksProxyAddressEnvVarName = "GF_SECURE_SOCKS_DATASOURCE_PROXY_PROXY_ADDRESS"
// Deprecated: PluginSecureSocksProxyServerNameEnvVarName is a constant for the GF_SECURE_SOCKS_DATASOURCE_PROXY_SERVER_NAME
// environment variable used to specify the server name of the secure socks proxy.
PluginSecureSocksProxyServerNameEnvVarName = "GF_SECURE_SOCKS_DATASOURCE_PROXY_SERVER_NAME"
// Deprecated: PluginSecureSocksProxyAllowInsecureEnvVarName is a constant for the GF_SECURE_SOCKS_DATASOURCE_PROXY_ALLOW_INSECURE
// environment variable used to specify if the proxy should use a TLS dialer.
PluginSecureSocksProxyAllowInsecureEnvVarName = "GF_SECURE_SOCKS_DATASOURCE_PROXY_ALLOW_INSECURE"
)

// Deprecated: clientCfgFromEnv gets the needed proxy information from the env variables that Grafana set with the values from the config ini
func clientCfgFromEnv() *ClientCfg {
if value, ok := os.LookupEnv(PluginSecureSocksProxyEnabledEnvVarName); ok {
enabled, err := strconv.ParseBool(value)
if err != nil || !enabled {
return nil
}
}

proxyAddress := ""
if value, ok := os.LookupEnv(PluginSecureSocksProxyAddressEnvVarName); ok {
proxyAddress = value
} else {
return nil
}

allowInsecure := false
if value, ok := os.LookupEnv(PluginSecureSocksProxyAllowInsecureEnvVarName); ok {
allowInsecure, _ = strconv.ParseBool(value)
}

// We only need to fill these fields on insecure mode.
if allowInsecure {
return &ClientCfg{
ProxyAddress: proxyAddress,
AllowInsecure: allowInsecure,
}
}

clientCert := ""
if value, ok := os.LookupEnv(PluginSecureSocksProxyClientCertFilePathEnvVarName); ok {
certPEMBlock, err := os.ReadFile(value)
if err != nil {
return nil
}
clientCert = string(certPEMBlock)
} else {
return nil
}

clientKey := ""
if value, ok := os.LookupEnv(PluginSecureSocksProxyClientKeyFilePathEnvVarName); ok {
keyPEMBlock, err := os.ReadFile(value)
if err != nil {
return nil
}
clientKey = string(keyPEMBlock)
} else {
return nil
}

var rootCAs []string
if value, ok := os.LookupEnv(PluginSecureSocksProxyRootCACertFilePathsEnvVarName); ok {
for _, rootCA := range strings.Split(value, " ") {
certPEMBlock, err := os.ReadFile(rootCA)
if err != nil {
return nil
}
rootCAs = append(rootCAs, string(certPEMBlock))
}
} else {
return nil
}

serverName := ""
if value, ok := os.LookupEnv(PluginSecureSocksProxyServerNameEnvVarName); ok {
serverName = value
} else {
return nil
}

return &ClientCfg{
ClientCert: clientCert,
ClientKey: clientKey,
RootCAs: rootCAs,
ProxyAddress: proxyAddress,
ServerName: serverName,
AllowInsecure: false,
}
}
122 changes: 122 additions & 0 deletions backend/proxy/env_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package proxy

import (
"fmt"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestClientCfgFromEnv(t *testing.T) {
// create empty file for testing configs
tempDir := t.TempDir()
testFilePath := filepath.Join(tempDir, "test")
testFileData := "foobar"
err := os.WriteFile(testFilePath, []byte(testFileData), 0600)
require.NoError(t, err)

cases := []struct {
description string
envVars map[string]string
expected *ClientCfg
}{
{
description: "socks proxy not enabled, should return nil",
envVars: map[string]string{
PluginSecureSocksProxyEnabledEnvVarName: "false",
PluginSecureSocksProxyAddressEnvVarName: "localhost",
PluginSecureSocksProxyClientCertFilePathEnvVarName: "cert",
PluginSecureSocksProxyClientKeyFilePathEnvVarName: "key",
PluginSecureSocksProxyRootCACertFilePathsEnvVarName: "root_ca",
PluginSecureSocksProxyServerNameEnvVarName: "server_name",
},
expected: nil,
},
{
description: "allowInsecure=true, should return config without tls fields filled",
envVars: map[string]string{
PluginSecureSocksProxyEnabledEnvVarName: "true",
PluginSecureSocksProxyAddressEnvVarName: "localhost",
PluginSecureSocksProxyAllowInsecureEnvVarName: "true",
},
expected: &ClientCfg{
ProxyAddress: "localhost",
AllowInsecure: true,
},
},
{
description: "allowInsecure=false, client cert is required, should return nil",
envVars: map[string]string{
PluginSecureSocksProxyEnabledEnvVarName: "true",
PluginSecureSocksProxyAddressEnvVarName: "localhost",
PluginSecureSocksProxyAllowInsecureEnvVarName: "false",
},
expected: nil,
},
{
description: "allowInsecure=false, client key is required, should return nil",
envVars: map[string]string{
PluginSecureSocksProxyEnabledEnvVarName: "true",
PluginSecureSocksProxyAddressEnvVarName: "localhost",
PluginSecureSocksProxyAllowInsecureEnvVarName: "false",
PluginSecureSocksProxyClientCertFilePathEnvVarName: "cert",
},
expected: nil,
},
{
description: "allowInsecure=false, root ca is required, should return nil",
envVars: map[string]string{
PluginSecureSocksProxyEnabledEnvVarName: "true",
PluginSecureSocksProxyAddressEnvVarName: "localhost",
PluginSecureSocksProxyAllowInsecureEnvVarName: "false",
PluginSecureSocksProxyClientCertFilePathEnvVarName: "cert",
PluginSecureSocksProxyClientKeyFilePathEnvVarName: "key",
},
expected: nil,
},
{
description: "allowInsecure=false, server name is required, should return nil",
envVars: map[string]string{
PluginSecureSocksProxyEnabledEnvVarName: "true",
PluginSecureSocksProxyAddressEnvVarName: "localhost",
PluginSecureSocksProxyAllowInsecureEnvVarName: "false",
PluginSecureSocksProxyClientCertFilePathEnvVarName: "cert",
PluginSecureSocksProxyClientKeyFilePathEnvVarName: "key",
PluginSecureSocksProxyRootCACertFilePathsEnvVarName: "root",
},
expected: nil,
},
{
description: "allowInsecure=false, should return config with tls fields filled",
envVars: map[string]string{
PluginSecureSocksProxyEnabledEnvVarName: "true",
PluginSecureSocksProxyAddressEnvVarName: "localhost",
PluginSecureSocksProxyAllowInsecureEnvVarName: "false",
PluginSecureSocksProxyClientCertFilePathEnvVarName: testFilePath,
PluginSecureSocksProxyClientKeyFilePathEnvVarName: testFilePath,
PluginSecureSocksProxyRootCACertFilePathsEnvVarName: fmt.Sprintf("%s %s", testFilePath, testFilePath),
PluginSecureSocksProxyServerNameEnvVarName: "name",
},
expected: &ClientCfg{
ProxyAddress: "localhost",
ClientCert: testFileData,
ClientKey: testFileData,
RootCAs: []string{testFileData, testFileData},
ServerName: "name",
AllowInsecure: false,
},
},
}

for _, tt := range cases {
t.Run(tt.description, func(t *testing.T) {
for key, value := range tt.envVars {
t.Setenv(key, value)
}
assert.Equal(t, tt.expected, clientCfgFromEnv())
})
}
}
2 changes: 1 addition & 1 deletion backend/proxy/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ func (o *Options) setDefaults() {
}

if o.ClientCfg == nil {
o.ClientCfg = getConfigFromEnv()
o.ClientCfg = clientCfgFromEnv()
}
}
Loading

0 comments on commit f88fae5

Please sign in to comment.