Skip to content

client: Allow Proxy Configuration in config.json#30588

Closed
dave-tucker wants to merge 1 commit intomoby:masterfrom
dave-tucker:proxyConfig
Closed

client: Allow Proxy Configuration in config.json#30588
dave-tucker wants to merge 1 commit intomoby:masterfrom
dave-tucker:proxyConfig

Conversation

@dave-tucker
Copy link
Contributor

@dave-tucker dave-tucker commented Jan 31, 2017

- What I did

Added a mechanism to allow HTTP/HTTPS/FTP/NO proxy variables to be configured in config.json. These are then automatically populated in a docker run command as environment variables or as build-args in docker build!

This makes it easier for users suffering behind an HTTP Proxy as they only need to configure this once as opposed to creating custom aliases or forgetting the args and being denied buildage/runnage.

Updates #30323

- How I did it

Extended the configfile with some new proxy related variables.

These are scoped per Docker Host with a default catchall.

The config file is then read by the build or run command and the necessary variables are exported.

A -e flag or --build-arg provided on the CLI will override these default settings.

- How to verify it

There are integration tests! But manually, you may:

  1. Create a config.json with { "proxies" : { "httpProxy" : "http://127.0.0.1:3001" }
  2. Create a Dockerfile
FROM busybox
RUN echo $HTTP_PROXY
CMD echo $HTTP_PROXY
  1. docker --config /path/to/config build -t configtest .
  2. Verify that the HTTP_PROXY variable is as configured
  3. docker run --rm configtest env
  4. Verify that the HTTP_PROXY variable is as configured

- Description for the changelog

Added HTTP/HTTPS/FTP proxy configuration to config.json

- A picture of a cute animal (not mandatory but encouraged)

@dave-tucker
Copy link
Contributor Author

Note: This will fail CI as the Unset test case is failing.
Strangely, this also fails with the same error when run without my patch e.g docker build -t foo --build-arg HTTP_PROXY .

@alexellis
Copy link
Contributor

I'd like to test this on our network connection config @ ADP. It's just what I was after. @dave-tucker

@alexellis
Copy link
Contributor

@dave-tucker

I can't build this behind a proxy without hacking all the Docker build scripts :-(

i.e.

-no-install-recommends 	&& pip install awscli==1.10.15
 ---> Running in 336264ab14d0
Ign http://deb.debian.org jessie InRelease
Ign http://security.debian.org jessie/updates InRelease
Ign http://ppa.launchpad.net trusty InRelease
Ign http://deb.debian.org jessie-updates InRelease
Err http://security.debian.org jessie/updates Release.gpg
  Connection failed [IP: 128.31.0.63 80]
Err http://ppa.launchpad.net trusty Release.gpg
  Connection failed
Err http://deb.debian.org jessie Release.gpg
  Connection failed [IP: 130.89.148.14 80]
Ign http://security.debian.org jessie/updates Release
Ign http://ppa.launchpad.net trusty Release
Err http://deb.debian.org jessie-updates Release.gpg
  Connection failed [IP: 5.153.231.4 80]

@dave-tucker
Copy link
Contributor Author

@alexellis let me see if I can get this passing CI so you can download a bundle from CI to test with

@alexellis
Copy link
Contributor

alexellis commented Feb 2, 2017

@dave-tucker I hacked the Makefile so that it passed --build args into the docker build command and make cross / install worked. I now have the unsupported build with your commit.

I edited a local config.json file, but it's not working for me (might be a user error)

Here's what I have:

$ ls
Dockerfile  config.json
$ docker --config config.json build -t curl .
WARNING: Error loading config file:config.json/config.json - stat config.json/config.json: not a directory

$ docker version
Client:
 Version:      1.14.0-dev
 API version:  1.26
 Go version:   go1.7.5
 Git commit:   43f3b63-unsupported
 Built:        Thu Feb  2 15:25:47 2017
 OS/Arch:      darwin/amd64

Server:
 Version:      1.14.0-dev
 API version:  1.26 (minimum version 1.12)
 Go version:   go1.7.5
 Git commit:   43f3b63-unsupported
 Built:        Thu Feb  2 15:25:47 2017
 OS/Arch:      linux/amd64
 Experimental: true

$ cat ./config.json
{ "httpProxy": "10.x.x.x:3128",
"httpsProxy": "10.x.x.x:3128"
}

@alexellis
Copy link
Contributor

alexellis commented Feb 2, 2017

Also tried editing ~/.docker/config.json which is what I think I needed to edit anyway. Do the new values belong at the root element of JSON? Still doesn't appear to work / pick-up values.

RUN env is showing up like this (without the http:// scheme, that I think is needed)

 ---> Running in fe5c3f911788
HTTPS_PROXY=x.x.x.x:3128
HOSTNAME=11fbdc1f630f
SHLVL=1
HOME=/root
https_proxy=10.x.x.x:3128
http_proxy=10.x.x.x:3128

@dave-tucker
Copy link
Contributor Author

I see bugs 🐛 Working on a fix @alexellis will ping you when it's ready for testing.

@dave-tucker dave-tucker force-pushed the proxyConfig branch 4 times, most recently from bb34d69 to f75c2e0 Compare February 3, 2017 03:38
Copy link
Contributor Author

@dave-tucker dave-tucker Feb 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps some Golang expert can help here as I'm out of my depth.

If instead of o.Set I initially tried m[k] = &v, however the value of m[k] outside of this function was always "".

This makes no sense to me as when I try to reproduce in the Go Playground I can't.
There is probably some subtle interaction happening here that I don't quite understand.

I'd like to fix this before merge so I can avoid calling ConvertKVStringsToMapWithNil twice.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, taking address from range variable doesn't work.

@dave-tucker
Copy link
Contributor Author

Ok @alexellis it's ready for testing. A couple more pointers:

  1. Yes this uses your ~/.docker/config.json by default. You can change this by passing the --config flag which takes a path to a directory where your config.json can be found (not the full path to config.json)

  2. Yes, httpProxy is part of the root in the JSON. You can also set httpsProxy, ftpProxy and noProxy depending on your environment.

  3. Yes, you should include the schema (e.g http://) in the value that's written to the httpProxy variable. This is directly mapped to the HTTP_PROXY and http_proxy build args. No additional validation is done so it's important to enter the right values!

@dave-tucker dave-tucker force-pushed the proxyConfig branch 2 times, most recently from 96d19f1 to e37c03c Compare February 3, 2017 19:13
@danielpanteleit
Copy link

@dave-tucker This would be really helpful in my case since I need to specify those build-args all the time.

I have two suggestions though:

  1. Usually (at least for me) the proxy settings are known, e.g. in /etc/environment, on the host. So I can use the following build-args without specifying the values:

--build-args={http{,s},ftp,no}_proxy,{HTTP{,S},FTP,NO}_PROXY

Wouldn't this be a typical use case? Maybe you can also add something like "systemProxy": bool to the config file?

  1. I have the same problem with docker run. It would be awesome if the same configuration would be used there too.

@dave-tucker
Copy link
Contributor Author

dave-tucker commented Feb 10, 2017

@danielpanteleit Thanks for the feedback

  1. Yes you are right, it would be nice to have an equivalent to the existing --build-args=HTTP_PROXY style syntax that will read the value configured in the environment. I'll look at adding it soon.

  2. Yes! If the approach in this PR is accepted by the maintainers then I also plan on extending this functionality to docker run too (and also scoping these configuration options per docker host).

@alexellis
Copy link
Contributor

Hey Dave, is this ready for testing yet? Do you need any help with the coding?

@dave-tucker
Copy link
Contributor Author

@alexellis ready for testing and passing CI.

@alexellis
Copy link
Contributor

Planning to try it out again this week. I'll test with this on DfM? Should also work on Linux client?

make cross
./contrib/mac-install-bundle.sh install

@dave-tucker
Copy link
Contributor Author

@alexellis yes it should work on any client - either on OSX or Linux

@alexellis
Copy link
Contributor

alexellis commented Feb 16, 2017

Showing client and server installed on DfM @dave-tucker :

/Users/aellis/go/src/github.com/docker/docker/bundles/latest/cross/darwin/amd64/docker version
Client:
 Version:      1.14.0-dev
 API version:  1.26
 Go version:   go1.7.5
 Git commit:   e37c03c-unsupported
 Built:        Wed Feb 15 02:11:56 2017
 OS/Arch:      darwin/amd64

Server:
 Version:      1.14.0-dev
 API version:  1.26 (minimum version 1.12)
 Go version:   go1.7.5
 Git commit:   e37c03c-unsupported
 Built:        Wed Feb 15 02:11:56 2017
 OS/Arch:      linux/amd64
 Experimental: true

With minimal Dockerfile this is working:

FROM alpine:latest

RUN apk add --update curl

Internet reachable:

  • build-args with known proxy
  • config.json with known proxy

Unreachable:

  • without build-args and no config.json
  • without build-args and invalid port for proxy

Looks good on DfM 👍

btw.. --config flag doesn't appear to accept a filename, just a folder.

@alexellis
Copy link
Contributor

@dave-tucker I could really do with this soon.. we're running some workshops with Docker and the instructions are getting way more complicated with having to detail --build-arg and -e for every docker build / docker run command.

What is the process for moving the change forward - does it need buy-in from more maintainers?

@dave-tucker
Copy link
Contributor Author

re: next steps, where are we at @thaJeztah?
do we need to discuss on the next maintainers meeting?

@alexellis
Copy link
Contributor

Yes, specifically for puling images from remote registries such as the Hub. I originally thought that was what the daemon.json option was going to be doing. Thanks for clarifying.

@alexellis
Copy link
Contributor

For the client; do we need a "default" option (i.e., a default for any daemon that is not explicitly defined in the client configuration)? We thought this could be added in a later stage if needed.

From a user experience point of view - at ADP all daemons needing internet access will have to go through a proxy, so a default option (now or in a later PR) would be a plus.

@alexellis
Copy link
Contributor

To prevent the proxy-settings from leaking into the image's metadata, any build-arg that is part of the "default" set of build args should not be included in the image history.

Definitely agree with this point.

@dave-tucker
Copy link
Contributor Author

dave-tucker commented Mar 6, 2017

Thanks @thaJeztah. In that case what I'll do is as follows:

I'll work on 3 PR's:

@alexellis your other use case is a tricky one 🤡

Implementation-wise... I see two options:

  1. Imagine that we create a new function ProxyFromConfigOrEnvironment that reads daemon.json to get proxy settings (with a fallback to ProxyFromEnvironment from net/http). We'd need to create a new transport using this and then make sure it was used anywhere that attempts HTTP connections. When I last looked, I wasn't sure how many places this was and/or whether all of this code was limited to the docker/docker repo - i.e what about communication with a registry which I think is handled in docker/distribution package. You might need to touch a wide range of API's to expose a means of providing your own transport 😢

  2. The other way would be to update the environment of dockerd PID. This would mean less changes but at a cost. I'm pretty sure many Linux aficionados will 😦 at changing the environment of a running process. There is also the issue of what happens to children of that PID - do we need to find all children and update their environments too. Assuming it works and is stable you'd have proxy hot-reload capability... but IMO it's quite a hacky approach.

TL;DR is that It's possible, but definitely a lot of work (read: I'd prefer to focus on my other PR's but if someone else wants to take a stab at it, great!)

My question is, is it worth the effort? We'd be deviating from a fairly standard practice (configuration of proxy via environment variables) which many people are already managing with Chef, Puppet, Salt, Ansible etc..

@dave-tucker dave-tucker changed the title Allow Proxy Configuration in config.json client: Allow Proxy Configuration in config.json Mar 6, 2017
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be misleading. Someone could look at these and think they're for all client connections and not just for use in the build as build-args. Could we prefix these with BuildArg, or something, to make this clear?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed this (patch set coming soon) to:

{ 
  "proxies": 
    {
      "default":
        {
          "httpProxy" : "http://proxy.example.com"
        }
     }
}

These will now be used for both docker build and docker run.

The top-level key can be easily changed to reflect this.
@alexellis suggested containerProxies but I'm undecided as yet

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we doing this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To set both upper and lowercase derivatives (e.g HTTP_PROXY and http_proxy) as some applications will only check for one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting, didn't notice before that we actually list both variants in the code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there's no official spec for those; some software uses lowercase other use uppercase 😞

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the json keys be all uppercase to match what people would have to type on the --build-arg option? Since case does matter (at least in some envs) I think using the right variant would reduce potential confusion and typos.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the convention is here. Personally I'd prefer http-proxy, https-proxy etc.. but from what I saw in the code it seems that camelCase is the done thing. Happy to change to whatever.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, I tend to view this less of a go or json convention and more one of consistency with how they would specify things elsewhere. For example, --build-arg http_PROXY=... should probably not be the same a --build-arg HTTP_PROXY=... since those really are two different env vars. So we should treat them as different in the config file too (assuming go allows us to - it might try to be "helpful" here and treat them the same :-( ). But we should document/code it the right way.

Copy link
Contributor

@duglin duglin Mar 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be really nice if the build args were nested under a "BuildArgs" property - then we wouldn't need to prefix them. But I'm not sure if our config file supports nested things.

@dave-tucker
Copy link
Contributor Author

ok! updated based on #30588 (comment)

This PR not only adds the config to config.json (scoped on a per daemon level with a default setting too), but it now works for both docker build and docker run!

This commit modifies config.json to allow for any proxies allowed in
build-args to be configured. These values will then be used
by default as build-args in docker build.

Signed-off-by: Dave Tucker <[email protected]>
@dave-tucker
Copy link
Contributor Author

Rebased but not able to get green tests locally. Seeing if I get the same result in CI...

Locally I get lots of errors like this:

START: docker_cli_build_test.go:4895: DockerSuite.TestBuildBuildTimeArgFromSpecificConfig
docker_cli_build_test.go:4916:
    result.Assert(c, icmd.Success)
/go/src/github.com/docker/docker/pkg/testutil/cmd/command.go:64:
    t.Fatalf("at %s:%d - %s", filepath.Base(file), line, err.Error())
... Error: at docker_cli_build_test.go:4916 -
Command:  /go/src/github.com/docker/docker/bundles/17.05.0-dev/dynbinary-client/docker -D --config /tmp/integration-cli-922980763 build -t bldargtest -
ExitCode: 1
Error:    exit status 1
Stdout:   Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM busybox
 ---> d9551b4026f0
Step 2/3 : RUN echo $HTTP_PROXY
 ---> Running in b9df6f422f82
http://proxy.example.com

Stderr:   unexpected EOF


Failures:
ExitCode was 1 expected 0
Expected no error

It's the --config file here that causes the issue. No idea why it didn't appear in earlier testing.
The error message is pretty generic 😿 although Duck Duck Go tells me that previous errors of this sort were related to permissions. Possibly related to changes in Docker for Mac but will reserve judgement until one CI has passed.

@thaJeztah thaJeztah added the status/failing-ci Indicates that the PR in its current state fails the test suite label Apr 6, 2017
@thaJeztah
Copy link
Member

@dave-tucker what's the status on this one; are you still working on this? Should we close in the meantime?

@dave-tucker
Copy link
Contributor Author

I don't think I'm going to get time to fix this before Dockercon so feel free to close for now...
Unless someone wants to carry this.
This was working fine... but there have been changes to the CLI and integration tests that seems to have broken it. It could also be my clumsy rebasing 😉

@thaJeztah
Copy link
Member

I think everyone is quite busy, so let me close this for now. But we definitely want this so, please remind us to reopen once you have some bandwidth again

@thaJeztah thaJeztah closed this Apr 6, 2017
@alexellis
Copy link
Contributor

Hmm this is the most useful PR for proxies and it's just been closed. Is the design review and implementation complete? What needs to be done to "finish it"?

@dave-tucker
Copy link
Contributor Author

@alexellis AFAIK the design was "ok" as it follows the guidelines set out in #30588 (comment)

All that needs doing here is to troubleshoot and fix the integration test failures.
FWIW, I built a binary and tested it manually and it still works as expected

@dave-tucker
Copy link
Contributor Author

@thaJeztah can you (or someone else with permission) re-open this PR?
The failing tests should be fixed in #32763
Once merged, I'll happily rebase to pick this up..
I've cherry-picked my other PR and run integration tests and they all pass 🎉

@alexellis
Copy link
Contributor

alexellis commented Apr 21, 2017 via email

@thaJeztah
Copy link
Member

Ugh, sorry didn't see your ping. GitHub doesn't allow me to reopen, possibly due to the repo-move (or did you push to the branch after it was closed?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status/failing-ci Indicates that the PR in its current state fails the test suite status/1-design-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants