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

Add http(s) proxy properties to daemon configuration (carry 42647) #42835

Merged
merged 6 commits into from
Oct 28, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/dockerd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) error {

flags.StringVar(&conf.DefaultRuntime, "default-runtime", config.StockRuntimeName, "Default OCI runtime for containers")

flags.StringVar(&conf.HTTPProxy, "http-proxy", "", "HTTP proxy URL to use for outgoing traffic")
flags.StringVar(&conf.HTTPSProxy, "https-proxy", "", "HTTPS proxy URL to use for outgoing traffic")
flags.StringVar(&conf.NoProxy, "no-proxy", "", "Comma-separated list of hosts or IP addresses for which the proxy is skipped")

return nil
}

Expand Down
28 changes: 28 additions & 0 deletions cmd/dockerd/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
return nil
}

configureProxyEnv(cli.Config)

warnOnDeprecatedConfigOptions(cli.Config)

if err := configureDaemonLogs(cli.Config); err != nil {
Expand Down Expand Up @@ -779,3 +781,29 @@ func configureDaemonLogs(conf *config.Config) error {
})
return nil
}

func configureProxyEnv(conf *config.Config) {
if p := conf.HTTPProxy; p != "" {
overrideProxyEnv("HTTP_PROXY", p)
overrideProxyEnv("http_proxy", p)
}
if p := conf.HTTPSProxy; p != "" {
overrideProxyEnv("HTTPS_PROXY", p)
overrideProxyEnv("https_proxy", p)
}
if p := conf.NoProxy; p != "" {
overrideProxyEnv("NO_PROXY", p)
overrideProxyEnv("no_proxy", p)
}
}

func overrideProxyEnv(name, val string) {
if oldVal := os.Getenv(name); oldVal != "" && oldVal != val {
logrus.WithFields(logrus.Fields{
"name": name,
"old-value": config.MaskCredentials(oldVal),
"new-value": config.MaskCredentials(val),
}).Warn("overriding existing proxy variable with value from configuration")
}
_ = os.Setenv(name, val)
}
24 changes: 24 additions & 0 deletions daemon/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"net"
"net/url"
"os"
"reflect"
"strings"
Expand Down Expand Up @@ -165,6 +166,7 @@ type CommonConfig struct {
ExecRoot string `json:"exec-root,omitempty"`
SocketGroup string `json:"group,omitempty"`
CorsHeaders string `json:"api-cors-header,omitempty"`
ProxyConfig

// TrustKeyPath is used to generate the daemon ID and for signing schema 1 manifests
// when pushing to a registry which does not support schema 2. This field is marked as
Expand Down Expand Up @@ -275,6 +277,13 @@ type CommonConfig struct {
DefaultRuntime string `json:"default-runtime,omitempty"`
}

// ProxyConfig holds the proxy-configuration for the daemon.
type ProxyConfig struct {
HTTPProxy string `json:"http-proxy,omitempty"`
HTTPSProxy string `json:"https-proxy,omitempty"`
NoProxy string `json:"no-proxy,omitempty"`
}

// IsValueSet returns true if a configuration value
// was explicitly set in the configuration file.
func (conf *Config) IsValueSet(name string) bool {
Expand Down Expand Up @@ -525,6 +534,11 @@ func findConfigurationConflicts(config map[string]interface{}, flags *pflag.Flag

var conflicts []string
printConflict := func(name string, flagValue, fileValue interface{}) string {
switch name {
case "http-proxy", "https-proxy":
flagValue = MaskCredentials(flagValue.(string))
fileValue = MaskCredentials(fileValue.(string))
}
return fmt.Sprintf("%s: (from flag: %v, from file: %v)", name, flagValue, fileValue)
}

Expand Down Expand Up @@ -645,3 +659,13 @@ func (conf *Config) GetDefaultRuntimeName() string {

return rt
}

// MaskCredentials masks credentials that are in an URL.
func MaskCredentials(rawURL string) string {
parsedURL, err := url.Parse(rawURL)
if err != nil || parsedURL.User == nil {
return rawURL
}
parsedURL.User = url.UserPassword("xxxxx", "xxxxx")
return parsedURL.String()
}
46 changes: 46 additions & 0 deletions daemon/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -578,3 +578,49 @@ func TestReloadWithDuplicateLabels(t *testing.T) {
err := Reload(configFile, flags, func(c *Config) {})
assert.Check(t, err)
}

func TestMaskURLCredentials(t *testing.T) {
tests := []struct {
rawURL string
maskedURL string
}{
{
rawURL: "",
maskedURL: "",
}, {
rawURL: "invalidURL",
maskedURL: "invalidURL",
}, {
rawURL: "http://proxy.example.com:80/",
maskedURL: "http://proxy.example.com:80/",
}, {
rawURL: "http://USER:PASSWORD@proxy.example.com:80/",
maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
}, {
rawURL: "http://PASSWORD:PASSWORD@proxy.example.com:80/",
maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
}, {
rawURL: "http://USER:@proxy.example.com:80/",
maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
}, {
rawURL: "http://:PASSWORD@proxy.example.com:80/",
maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
}, {
rawURL: "http://USER@docker:password@proxy.example.com:80/",
maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
}, {
rawURL: "http://USER%40docker:password@proxy.example.com:80/",
maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
}, {
rawURL: "http://USER%40docker:pa%3Fsword@proxy.example.com:80/",
maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
}, {
rawURL: "http://USER%40docker:pa%3Fsword@proxy.example.com:80/hello%20world",
maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/hello%20world",
},
}
for _, test := range tests {
maskedURL := MaskCredentials(test.rawURL)
assert.Equal(t, maskedURL, test.maskedURL)
}
}
24 changes: 10 additions & 14 deletions daemon/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package daemon // import "github.com/docker/docker/daemon"

import (
"fmt"
"net/url"
"os"
"runtime"
"strings"
Expand Down Expand Up @@ -64,9 +63,9 @@ func (daemon *Daemon) SystemInfo() *types.Info {
Labels: daemon.configStore.Labels,
ExperimentalBuild: daemon.configStore.Experimental,
ServerVersion: dockerversion.Version,
HTTPProxy: maskCredentials(getEnvAny("HTTP_PROXY", "http_proxy")),
HTTPSProxy: maskCredentials(getEnvAny("HTTPS_PROXY", "https_proxy")),
NoProxy: getEnvAny("NO_PROXY", "no_proxy"),
HTTPProxy: config.MaskCredentials(getConfigOrEnv(daemon.configStore.HTTPProxy, "HTTP_PROXY", "http_proxy")),
HTTPSProxy: config.MaskCredentials(getConfigOrEnv(daemon.configStore.HTTPSProxy, "HTTPS_PROXY", "https_proxy")),
NoProxy: getConfigOrEnv(daemon.configStore.NoProxy, "NO_PROXY", "no_proxy"),
LiveRestoreEnabled: daemon.configStore.LiveRestoreEnabled,
Isolation: daemon.defaultIsolation,
}
Expand Down Expand Up @@ -289,16 +288,6 @@ func osVersion() (version string) {
return version
}

func maskCredentials(rawURL string) string {
parsedURL, err := url.Parse(rawURL)
if err != nil || parsedURL.User == nil {
return rawURL
}
parsedURL.User = url.UserPassword("xxxxx", "xxxxx")
maskedURL := parsedURL.String()
return maskedURL
}

func getEnvAny(names ...string) string {
for _, n := range names {
if val := os.Getenv(n); val != "" {
Expand All @@ -307,3 +296,10 @@ func getEnvAny(names ...string) string {
}
return ""
}

func getConfigOrEnv(config string, env ...string) string {
if config != "" {
return config
}
return getEnvAny(env...)
}
53 changes: 0 additions & 53 deletions daemon/info_test.go

This file was deleted.

16 changes: 14 additions & 2 deletions daemon/reload.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,26 @@ func (daemon *Daemon) Reload(conf *config.Config) (err error) {
attributes := map[string]string{}

defer func() {
jsonString, _ := json.Marshal(daemon.configStore)
if err == nil {
jsonString, _ := json.Marshal(&struct {
*config.Config
config.ProxyConfig
}{
Config: daemon.configStore,
ProxyConfig: config.ProxyConfig{
HTTPProxy: config.MaskCredentials(daemon.configStore.HTTPProxy),
HTTPSProxy: config.MaskCredentials(daemon.configStore.HTTPSProxy),
NoProxy: config.MaskCredentials(daemon.configStore.NoProxy),
},
})
logrus.Infof("Reloaded configuration: %s", jsonString)
}

// we're unlocking here, because
// LogDaemonEventWithAttributes() -> SystemInfo() -> GetAllRuntimes()
// holds that lock too.
daemon.configStore.Unlock()
if err == nil {
logrus.Infof("Reloaded configuration: %s", jsonString)
daemon.LogDaemonEventWithAttributes("reload", attributes)
}
}()
Expand Down