Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.1.2 Backport] CBG-3509: Add unsupported.allow_dbconfig_env_vars #6501

Merged
merged 1 commit into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rest/config_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ func registerConfigFlags(config *StartupConfig, fs *flag.FlagSet) map[string]con
"unsupported.serverless.enabled": {&config.Unsupported.Serverless.Enabled, fs.Bool("unsupported.serverless.enabled", false, "Settings for running Sync Gateway in serverless mode.")},
"unsupported.serverless.min_config_fetch_interval": {&config.Unsupported.Serverless.MinConfigFetchInterval, fs.String("unsupported.serverless.min_config_fetch_interval", "", "How long to cache configs fetched from the buckets for. This cache is used for requested databases that SG does not know about.")},
"unsupported.use_xattr_config": {&config.Unsupported.UseXattrConfig, fs.Bool("unsupported.use_xattr_config", false, "Store database configurations in system xattrs")},
"unsupported.allow_dbconfig_env_vars": {&config.Unsupported.AllowDbConfigEnvVars, fs.Bool("unsupported.allow_dbconfig_env_vars", true, "Can be set to false to skip environment variable expansion in database configs")},

"unsupported.user_queries": {&config.Unsupported.UserQueries, fs.Bool("unsupported.user_queries", false, "Whether user-query APIs are enabled")},

Expand Down
14 changes: 8 additions & 6 deletions rest/config_startup.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func DefaultStartupConfig(defaultLogFilePath string) StartupConfig {
Enabled: base.BoolPtr(false),
MinConfigFetchInterval: base.NewConfigDuration(DefaultMinConfigFetchInterval),
},
AllowDbConfigEnvVars: base.BoolPtr(true),
},
MaxFileDescriptors: DefaultMaxFileDescriptors,
}
Expand Down Expand Up @@ -141,12 +142,13 @@ type ReplicatorConfig struct {
}

type UnsupportedConfig struct {
StatsLogFrequency *base.ConfigDuration `json:"stats_log_frequency,omitempty" help:"How often should stats be written to stats logs"`
UseStdlibJSON *bool `json:"use_stdlib_json,omitempty" help:"Bypass the jsoniter package and use Go's stdlib instead"`
Serverless ServerlessConfig `json:"serverless,omitempty"`
HTTP2 *HTTP2Config `json:"http2,omitempty"`
UserQueries *bool `json:"user_queries,omitempty" help:"Feature flag for user N1QL/JS/GraphQL queries"`
UseXattrConfig *bool `json:"use_xattr_config,omitempty" help:"Store database configurations in system xattrs"`
StatsLogFrequency *base.ConfigDuration `json:"stats_log_frequency,omitempty" help:"How often should stats be written to stats logs"`
UseStdlibJSON *bool `json:"use_stdlib_json,omitempty" help:"Bypass the jsoniter package and use Go's stdlib instead"`
Serverless ServerlessConfig `json:"serverless,omitempty"`
HTTP2 *HTTP2Config `json:"http2,omitempty"`
UserQueries *bool `json:"user_queries,omitempty" help:"Feature flag for user N1QL/JS/GraphQL queries"`
UseXattrConfig *bool `json:"use_xattr_config,omitempty" help:"Store database configurations in system xattrs"`
AllowDbConfigEnvVars *bool `json:"allow_dbconfig_env_vars,omitempty" help:"Can be set to false to skip environment variable expansion in database configs"`
}

type ServerlessConfig struct {
Expand Down
80 changes: 80 additions & 0 deletions rest/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,86 @@ func TestExpandEnv(t *testing.T) {
}
}

// TestDbConfigEnvVarsToggle ensures that AllowDbConfigEnvVars toggles the ability to use env vars in db config.
func TestDbConfigEnvVarsToggle(t *testing.T) {
// Set up an env var with a secret value and use it as a channel name to assert its value.
const (
varName = "SECRET_SG_TEST_VAR"
secretVal = "secret"
defaultVal = "substitute"
)
// ${SECRET_SG_TEST_VAR:-substitute}
unexpandedVal := fmt.Sprintf("${%s:-%s}", varName, defaultVal)

tests := []struct {
allowDbConfigEnvVars *bool
setEnvVar bool
expectedChannel string
unexpectedChannels []string
}{
{
allowDbConfigEnvVars: nil, // defaults to true - so use nil to check default handling
setEnvVar: true,
expectedChannel: secretVal,
unexpectedChannels: []string{defaultVal, unexpandedVal},
},
{
allowDbConfigEnvVars: base.BoolPtr(true),
setEnvVar: false,
expectedChannel: defaultVal,
unexpectedChannels: []string{secretVal, unexpandedVal},
},
{
allowDbConfigEnvVars: base.BoolPtr(false),
setEnvVar: true,
expectedChannel: unexpandedVal,
unexpectedChannels: []string{secretVal, defaultVal},
},
}

for _, test := range tests {
t.Run(fmt.Sprintf("allowDbConfigEnvVars=%v_setEnvVar=%v", test.allowDbConfigEnvVars, test.setEnvVar), func(t *testing.T) {
rt := NewRestTesterDefaultCollection(t, &RestTesterConfig{
PersistentConfig: true,
allowDbConfigEnvVars: test.allowDbConfigEnvVars,
})
defer rt.Close()

if test.setEnvVar {
require.NoError(t, os.Setenv(varName, secretVal))
t.Cleanup(func() {
require.NoError(t, os.Unsetenv(varName))
})
}

dbc := rt.NewDbConfig()
dbc.Sync = base.StringPtr(fmt.Sprintf(
"function(doc) {channel('%s');}",
unexpandedVal,
))

resp := rt.CreateDatabase("db", dbc)
AssertStatus(t, resp, http.StatusCreated)

docID := "doc"
putDocResp := rt.PutDoc(docID, `{"foo":"bar"}`)
require.True(t, putDocResp.Ok)

require.NoError(t, rt.WaitForPendingChanges())

// ensure doc is in expected channel and is not in the unexpected channels
changes, err := rt.WaitForChanges(1, "/{{.keyspace}}/_changes?filter=sync_gateway/bychannel&channels="+test.expectedChannel, "", true)
require.NoError(t, err)
changes.RequireDocIDs(t, []string{docID})

channels := strings.Join(test.unexpectedChannels, ",")
changes, err = rt.WaitForChanges(0, "/{{.keyspace}}/_changes?filter=sync_gateway/bychannel&channels="+channels, "", true)
require.NoError(t, err)
changes.RequireDocIDs(t, []string{})
})
}
}

// createTempFile creates a temporary file with the given content.
func createTempFile(t *testing.T, content []byte) *os.File {
file, err := os.CreateTemp("", "*-sync_gateway.conf")
Expand Down
9 changes: 6 additions & 3 deletions rest/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1036,10 +1036,13 @@ func (h *handler) readSanitizeJSON(val interface{}) error {
}

// Expand environment variables.
content, err = expandEnv(h.ctx(), content)
if err != nil {
return err
if base.BoolDefault(h.server.Config.Unsupported.AllowDbConfigEnvVars, true) {
content, err = expandEnv(h.ctx(), content)
if err != nil {
return err
}
}

// Convert the back quotes into double-quotes, escapes literal
// backslashes, newlines or double-quotes with backslashes.
content = base.ConvertBackQuotedStrings(content)
Expand Down
1 change: 1 addition & 0 deletions rest/utilities_testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ func (rt *RestTester) Bucket() base.Bucket {
sc.Bootstrap.UseTLSServer = &rt.RestTesterConfig.useTLSServer
sc.Bootstrap.ServerTLSSkipVerify = base.BoolPtr(base.TestTLSSkipVerify())
sc.Unsupported.Serverless.Enabled = &rt.serverless
sc.Unsupported.AllowDbConfigEnvVars = rt.RestTesterConfig.allowDbConfigEnvVars
if rt.serverless {
if !rt.PersistentConfig {
rt.TB.Fatalf("Persistent config must be used when running in serverless mode")
Expand Down