-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add http(s) proxy properties to daemon configuration
This allows configuring the daemon's proxy server through the daemon.json con- figuration file or command-line flags configuration file, in addition to the existing option (through environment variables). Configuring environment variables on Windows to configure a service is more complicated than on Linux, and adding alternatives for this to the daemon con- figuration makes the configuration more transparent and easier to use. The configuration as set through command-line flags or through the daemon.json configuration file takes precedence over env-vars in the daemon's environment, which allows the daemon to use a different proxy. If both command-line flags and a daemon.json configuration option is set, an error is produced when starting the daemon. Note that this configuration is not "live reloadable" due to Golang's use of `sync.Once()` for proxy configuration, which means that changing the proxy configuration requires a restart of the daemon (reload / SIGHUP will not update the configuration. With this patch: cat /etc/docker/daemon.json { "http-proxy": "http://proxytest.example.com:80", "https-proxy": "https://proxytest.example.com:443" } docker pull busybox Using default tag: latest Error response from daemon: Get "https://registry-1.docker.io/v2/": proxyconnect tcp: dial tcp: lookup proxytest.example.com on 127.0.0.11:53: no such host docker build . Sending build context to Docker daemon 89.28MB Step 1/3 : FROM golang:1.16-alpine AS base Get "https://registry-1.docker.io/v2/": proxyconnect tcp: dial tcp: lookup proxytest.example.com on 127.0.0.11:53: no such host Integration tests were added to test the behavior: - verify that the configuration through all means are used (env-var, command-line flags, damon.json), and used in the expected order of preference. - verify that conflicting options produce an error. Signed-off-by: Anca Iordache <[email protected]> Signed-off-by: Sebastiaan van Stijn <[email protected]>
- Loading branch information
Showing
5 changed files
with
203 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,22 @@ | ||
package daemon // import "github.com/docker/docker/integration/daemon" | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
"net/http/httptest" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"runtime" | ||
"testing" | ||
|
||
"github.com/docker/docker/api/types" | ||
"github.com/docker/docker/daemon/config" | ||
"github.com/docker/docker/testutil/daemon" | ||
"gotest.tools/v3/assert" | ||
is "gotest.tools/v3/assert/cmp" | ||
"gotest.tools/v3/env" | ||
"gotest.tools/v3/skip" | ||
) | ||
|
||
|
@@ -146,3 +152,150 @@ func TestConfigDaemonSeccompProfiles(t *testing.T) { | |
}) | ||
} | ||
} | ||
|
||
func TestDaemonProxy(t *testing.T) { | ||
skip.If(t, runtime.GOOS == "windows", "cannot start multiple daemons on windows") | ||
|
||
var received string | ||
proxyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
received = r.Host | ||
w.Header().Set("Content-Type", "application/json") | ||
_, _ = w.Write([]byte("OK")) | ||
})) | ||
defer proxyServer.Close() | ||
|
||
// Configure proxy through env-vars | ||
t.Run("environment variables", func(t *testing.T) { | ||
defer env.Patch(t, "HTTP_PROXY", proxyServer.URL)() | ||
defer env.Patch(t, "HTTPS_PROXY", proxyServer.URL)() | ||
defer env.Patch(t, "NO_PROXY", "example.com")() | ||
|
||
d := daemon.New(t) | ||
c := d.NewClientT(t) | ||
defer func() { _ = c.Close() }() | ||
ctx := context.Background() | ||
d.Start(t) | ||
|
||
_, err := c.ImagePull(ctx, "example.org:5000/some/image:latest", types.ImagePullOptions{}) | ||
assert.ErrorContains(t, err, "", "pulling should have failed") | ||
assert.Equal(t, received, "example.org:5000") | ||
|
||
// Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed. | ||
_, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{}) | ||
assert.ErrorContains(t, err, "", "pulling should have failed") | ||
assert.Equal(t, received, "example.org:5000", "should not have used proxy") | ||
|
||
info := d.Info(t) | ||
assert.Equal(t, info.HTTPProxy, proxyServer.URL) | ||
assert.Equal(t, info.HTTPSProxy, proxyServer.URL) | ||
assert.Equal(t, info.NoProxy, "example.com") | ||
d.Stop(t) | ||
}) | ||
|
||
// Configure proxy through command-line flags | ||
t.Run("command-line options", func(t *testing.T) { | ||
defer env.Patch(t, "HTTP_PROXY", "http://from-env-http.invalid")() | ||
defer env.Patch(t, "http_proxy", "http://from-env-http.invalid")() | ||
defer env.Patch(t, "HTTPS_PROXY", "https://from-env-https.invalid")() | ||
defer env.Patch(t, "https_proxy", "https://from-env-http.invalid")() | ||
defer env.Patch(t, "NO_PROXY", "ignore.invalid")() | ||
defer env.Patch(t, "no_proxy", "ignore.invalid")() | ||
|
||
d := daemon.New(t) | ||
d.Start(t, "--http-proxy", proxyServer.URL, "--https-proxy", proxyServer.URL, "--no-proxy", "example.com") | ||
|
||
logs, err := d.ReadLogFile() | ||
assert.NilError(t, err) | ||
assert.Assert(t, is.Contains(string(logs), "overriding existing proxy variable with value from configuration")) | ||
for _, v := range []string{"http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY", "no_proxy", "NO_PROXY"} { | ||
assert.Assert(t, is.Contains(string(logs), "name="+v)) | ||
} | ||
|
||
c := d.NewClientT(t) | ||
defer func() { _ = c.Close() }() | ||
ctx := context.Background() | ||
|
||
_, err = c.ImagePull(ctx, "example.org:5001/some/image:latest", types.ImagePullOptions{}) | ||
assert.ErrorContains(t, err, "", "pulling should have failed") | ||
assert.Equal(t, received, "example.org:5001") | ||
|
||
// Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed. | ||
_, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{}) | ||
assert.ErrorContains(t, err, "", "pulling should have failed") | ||
assert.Equal(t, received, "example.org:5001", "should not have used proxy") | ||
|
||
info := d.Info(t) | ||
assert.Equal(t, info.HTTPProxy, proxyServer.URL) | ||
assert.Equal(t, info.HTTPSProxy, proxyServer.URL) | ||
assert.Equal(t, info.NoProxy, "example.com") | ||
|
||
d.Stop(t) | ||
}) | ||
|
||
// Configure proxy through configuration file | ||
t.Run("configuration file", func(t *testing.T) { | ||
defer env.Patch(t, "HTTP_PROXY", "http://from-env-http.invalid")() | ||
defer env.Patch(t, "http_proxy", "http://from-env-http.invalid")() | ||
defer env.Patch(t, "HTTPS_PROXY", "https://from-env-https.invalid")() | ||
defer env.Patch(t, "https_proxy", "https://from-env-http.invalid")() | ||
defer env.Patch(t, "NO_PROXY", "ignore.invalid")() | ||
defer env.Patch(t, "no_proxy", "ignore.invalid")() | ||
|
||
d := daemon.New(t) | ||
c := d.NewClientT(t) | ||
defer func() { _ = c.Close() }() | ||
ctx := context.Background() | ||
|
||
configFile := filepath.Join(d.RootDir(), "daemon.json") | ||
configJSON := fmt.Sprintf(`{"http-proxy":%[1]q, "https-proxy": %[1]q, "no-proxy": "example.com"}`, proxyServer.URL) | ||
assert.NilError(t, os.WriteFile(configFile, []byte(configJSON), 0644)) | ||
|
||
d.Start(t, "--config-file", configFile) | ||
|
||
logs, err := d.ReadLogFile() | ||
assert.NilError(t, err) | ||
assert.Assert(t, is.Contains(string(logs), "overriding existing proxy variable with value from configuration")) | ||
for _, v := range []string{"http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY", "no_proxy", "NO_PROXY"} { | ||
assert.Assert(t, is.Contains(string(logs), "name="+v)) | ||
} | ||
|
||
_, err = c.ImagePull(ctx, "example.org:5002/some/image:latest", types.ImagePullOptions{}) | ||
assert.ErrorContains(t, err, "", "pulling should have failed") | ||
assert.Equal(t, received, "example.org:5002") | ||
|
||
// Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed. | ||
_, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{}) | ||
assert.ErrorContains(t, err, "", "pulling should have failed") | ||
assert.Equal(t, received, "example.org:5002", "should not have used proxy") | ||
|
||
info := d.Info(t) | ||
assert.Equal(t, info.HTTPProxy, proxyServer.URL) | ||
assert.Equal(t, info.HTTPSProxy, proxyServer.URL) | ||
assert.Equal(t, info.NoProxy, "example.com") | ||
|
||
d.Stop(t) | ||
}) | ||
|
||
// Conflicting options (passed both through command-line options and config file) | ||
t.Run("conflicting options", func(t *testing.T) { | ||
const ( | ||
proxyRawURL = "https://myuser:[email protected]" | ||
) | ||
|
||
d := daemon.New(t) | ||
|
||
configFile := filepath.Join(d.RootDir(), "daemon.json") | ||
configJSON := fmt.Sprintf(`{"http-proxy":%[1]q, "https-proxy": %[1]q, "no-proxy": "example.com"}`, proxyRawURL) | ||
assert.NilError(t, os.WriteFile(configFile, []byte(configJSON), 0644)) | ||
|
||
err := d.StartWithError("--http-proxy", proxyRawURL, "--https-proxy", proxyRawURL, "--no-proxy", "example.com", "--config-file", configFile, "--validate") | ||
assert.ErrorContains(t, err, "daemon exited during startup") | ||
logs, err := d.ReadLogFile() | ||
assert.NilError(t, err) | ||
expected := fmt.Sprintf( | ||
`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)`, | ||
proxyRawURL, | ||
) | ||
assert.Assert(t, is.Contains(string(logs), expected)) | ||
}) | ||
} |