Skip to content
This repository was archived by the owner on Oct 13, 2023. It is now read-only.

Commit 76016b8

Browse files
committed
daemon: make sure proxy settings are sanitized when printing
The daemon can print the proxy configuration as part of error-messages, and when reloading the daemon configuration (SIGHUP). Make sure that the configuration is sanitized before printing. Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent bad4b30 commit 76016b8

File tree

4 files changed

+68
-14
lines changed

4 files changed

+68
-14
lines changed

cmd/dockerd/daemon.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,8 +801,8 @@ func overrideProxyEnv(name, val string) {
801801
if oldVal := os.Getenv(name); oldVal != "" && oldVal != val {
802802
logrus.WithFields(logrus.Fields{
803803
"name": name,
804-
"old-value": oldVal,
805-
"new-value": val,
804+
"old-value": config.MaskCredentials(oldVal),
805+
"new-value": config.MaskCredentials(val),
806806
}).Warn("overriding existing proxy variable with value from configuration")
807807
}
808808
_ = os.Setenv(name, val)

daemon/config/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,11 @@ func findConfigurationConflicts(config map[string]interface{}, flags *pflag.Flag
534534

535535
var conflicts []string
536536
printConflict := func(name string, flagValue, fileValue interface{}) string {
537+
switch name {
538+
case "http-proxy", "https-proxy":
539+
flagValue = MaskCredentials(flagValue.(string))
540+
fileValue = MaskCredentials(fileValue.(string))
541+
}
537542
return fmt.Sprintf("%s: (from flag: %v, from file: %v)", name, flagValue, fileValue)
538543
}
539544

daemon/reload.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,26 @@ func (daemon *Daemon) Reload(conf *config.Config) (err error) {
2828
attributes := map[string]string{}
2929

3030
defer func() {
31-
jsonString, _ := json.Marshal(daemon.configStore)
31+
if err == nil {
32+
jsonString, _ := json.Marshal(&struct {
33+
*config.Config
34+
config.ProxyConfig
35+
}{
36+
Config: daemon.configStore,
37+
ProxyConfig: config.ProxyConfig{
38+
HTTPProxy: config.MaskCredentials(daemon.configStore.HTTPProxy),
39+
HTTPSProxy: config.MaskCredentials(daemon.configStore.HTTPSProxy),
40+
NoProxy: config.MaskCredentials(daemon.configStore.NoProxy),
41+
},
42+
})
43+
logrus.Infof("Reloaded configuration: %s", jsonString)
44+
}
3245

3346
// we're unlocking here, because
3447
// LogDaemonEventWithAttributes() -> SystemInfo() -> GetAllRuntimes()
3548
// holds that lock too.
3649
daemon.configStore.Unlock()
3750
if err == nil {
38-
logrus.Infof("Reloaded configuration: %s", jsonString)
3951
daemon.LogDaemonEventWithAttributes("reload", attributes)
4052
}
4153
}()

integration/daemon/daemon_test.go

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"os/exec"
1010
"path/filepath"
1111
"runtime"
12+
"strings"
13+
"syscall"
1214
"testing"
1315

1416
"github.com/docker/docker/api/types"
@@ -165,6 +167,8 @@ func TestDaemonProxy(t *testing.T) {
165167
}))
166168
defer proxyServer.Close()
167169

170+
const userPass = "myuser:mypassword@"
171+
168172
// Configure proxy through env-vars
169173
t.Run("environment variables", func(t *testing.T) {
170174
defer env.Patch(t, "HTTP_PROXY", proxyServer.URL)()
@@ -195,10 +199,10 @@ func TestDaemonProxy(t *testing.T) {
195199

196200
// Configure proxy through command-line flags
197201
t.Run("command-line options", func(t *testing.T) {
198-
defer env.Patch(t, "HTTP_PROXY", "http://from-env-http.invalid")()
199-
defer env.Patch(t, "http_proxy", "http://from-env-http.invalid")()
200-
defer env.Patch(t, "HTTPS_PROXY", "https://from-env-https.invalid")()
201-
defer env.Patch(t, "https_proxy", "https://from-env-http.invalid")()
202+
defer env.Patch(t, "HTTP_PROXY", "http://"+userPass+"from-env-http.invalid")()
203+
defer env.Patch(t, "http_proxy", "http://"+userPass+"from-env-http.invalid")()
204+
defer env.Patch(t, "HTTPS_PROXY", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")()
205+
defer env.Patch(t, "https_proxy", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")()
202206
defer env.Patch(t, "NO_PROXY", "ignore.invalid")()
203207
defer env.Patch(t, "no_proxy", "ignore.invalid")()
204208

@@ -210,6 +214,7 @@ func TestDaemonProxy(t *testing.T) {
210214
assert.Assert(t, is.Contains(string(logs), "overriding existing proxy variable with value from configuration"))
211215
for _, v := range []string{"http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY", "no_proxy", "NO_PROXY"} {
212216
assert.Assert(t, is.Contains(string(logs), "name="+v))
217+
assert.Assert(t, !strings.Contains(string(logs), userPass), "logs should not contain the non-sanitized proxy URL: %s", string(logs))
213218
}
214219

215220
c := d.NewClientT(t)
@@ -235,10 +240,10 @@ func TestDaemonProxy(t *testing.T) {
235240

236241
// Configure proxy through configuration file
237242
t.Run("configuration file", func(t *testing.T) {
238-
defer env.Patch(t, "HTTP_PROXY", "http://from-env-http.invalid")()
239-
defer env.Patch(t, "http_proxy", "http://from-env-http.invalid")()
240-
defer env.Patch(t, "HTTPS_PROXY", "https://from-env-https.invalid")()
241-
defer env.Patch(t, "https_proxy", "https://from-env-http.invalid")()
243+
defer env.Patch(t, "HTTP_PROXY", "http://"+userPass+"from-env-http.invalid")()
244+
defer env.Patch(t, "http_proxy", "http://"+userPass+"from-env-http.invalid")()
245+
defer env.Patch(t, "HTTPS_PROXY", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")()
246+
defer env.Patch(t, "https_proxy", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")()
242247
defer env.Patch(t, "NO_PROXY", "ignore.invalid")()
243248
defer env.Patch(t, "no_proxy", "ignore.invalid")()
244249

@@ -258,6 +263,7 @@ func TestDaemonProxy(t *testing.T) {
258263
assert.Assert(t, is.Contains(string(logs), "overriding existing proxy variable with value from configuration"))
259264
for _, v := range []string{"http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY", "no_proxy", "NO_PROXY"} {
260265
assert.Assert(t, is.Contains(string(logs), "name="+v))
266+
assert.Assert(t, !strings.Contains(string(logs), userPass), "logs should not contain the non-sanitized proxy URL: %s", string(logs))
261267
}
262268

263269
_, err = c.ImagePull(ctx, "example.org:5002/some/image:latest", types.ImagePullOptions{})
@@ -280,7 +286,8 @@ func TestDaemonProxy(t *testing.T) {
280286
// Conflicting options (passed both through command-line options and config file)
281287
t.Run("conflicting options", func(t *testing.T) {
282288
const (
283-
proxyRawURL = "https://myuser:[email protected]"
289+
proxyRawURL = "https://" + userPass + "example.org"
290+
proxyURL = "https://xxxxx:[email protected]"
284291
)
285292

286293
d := daemon.New(t)
@@ -295,8 +302,38 @@ func TestDaemonProxy(t *testing.T) {
295302
assert.NilError(t, err)
296303
expected := fmt.Sprintf(
297304
`the following directives are specified both as a flag and in the configuration file: http-proxy: (from flag: %[1]s, from file: %[1]s), https-proxy: (from flag: %[1]s, from file: %[1]s), no-proxy: (from flag: example.com, from file: example.com)`,
298-
proxyRawURL,
305+
proxyURL,
299306
)
300307
assert.Assert(t, is.Contains(string(logs), expected))
301308
})
309+
310+
// Make sure values are sanitized when reloading the daemon-config
311+
t.Run("reload sanitized", func(t *testing.T) {
312+
const (
313+
proxyRawURL = "https://" + userPass + "example.org"
314+
proxyURL = "https://xxxxx:[email protected]"
315+
)
316+
317+
d := daemon.New(t)
318+
d.Start(t, "--http-proxy", proxyRawURL, "--https-proxy", proxyRawURL, "--no-proxy", "example.com")
319+
defer d.Stop(t)
320+
err := d.Signal(syscall.SIGHUP)
321+
assert.NilError(t, err)
322+
323+
logs, err := d.ReadLogFile()
324+
assert.NilError(t, err)
325+
326+
// FIXME: there appears to ba a race condition, which causes ReadLogFile
327+
// to not contain the full logs after signaling the daemon to reload,
328+
// causing the test to fail here. As a workaround, check if we
329+
// received the "reloaded" message after signaling, and only then
330+
// check that it's sanitized properly. For more details on this
331+
// issue, see https://github.com/moby/moby/pull/42835/files#r713120315
332+
if !strings.Contains(string(logs), "Reloaded configuration:") {
333+
t.Skip("Skipping test, because we did not find 'Reloaded configuration' in the logs")
334+
}
335+
336+
assert.Assert(t, is.Contains(string(logs), proxyURL))
337+
assert.Assert(t, !strings.Contains(string(logs), userPass), "logs should not contain the non-sanitized proxy URL: %s", string(logs))
338+
})
302339
}

0 commit comments

Comments
 (0)