Development and Proxies

Development and Proxies

I'm in the unfortunate circumstance to be using a mandatory proxy these days (including SSL) and unlike with browsers where it's kind of fire and forget, if you're developing software there's a plethora of tools that will or will not accept the default environment variables, so here's a list of stuff and how to fix it.

Linux environment variables

The usual proxy variables:

PROXY="http://proxy.local:8080"

export http_proxy="$PROXY"
export HTTP_PROXY="$PROXY"
export https_proxy="$PROXY"
export HTTPS_PROXY="$PROXY"

export no_proxy='localhost,127.0.0.1,*.localstuff.example.org'
export NO_PROXY='localhost,127.0.0.1,*.localstuff.example.org'

Interestingly the internet seems to agree or disagree if it's the uppercase version or the lowercase version. I think there's no harm in setting all of them and just not thinking about it anymore.

Fortunately this solves the issues for all tools and package managers that use curl under the hood.

I've since configured these additional ones:

nix-pkgs

MY_CA_CERT=/foo/my-cert.crt

export NIX_SSL_CERT_FILE="$MY_CA_CERT"

Although on my current machine I actually have it set to /etc/ssl/certs/ca-certificates.crt

Ubuntu/Debian

As an aside, on Ubuntu you can trust your org's CA cert like this:

$ sudo cp MyOrgCA.crt /usr/local/share/ca-certificates/MyOrgCA.crt
$ sudo update-ca-certificates

RedHat/CentOS/AmazonLinux

This should work on all RH flavours:

$ sudo cp MyOrgCA.crt /etc/pki/ca-trust/source/anchors/
$ sudo update-ca-trust extract

elixir/mix

So this week I tried to install the Phoenix framework and that was a journey.

Apparently kerl and kiex work with curl, so that was no problem.

The fun started with mix where I think it's not documented properly, or at least their docs aren't ranking high enough, so I first arrived at

export HEX_UNSAFE_HTTPS=1, which is a bad idea, so don't do that.

The actual solution seems to be:

export HEX_CACERTS_PATH="$MY_CA_CERT"

But then the next riddle came up, mix phx.server in Phoenix's hello world example seemed to be downloading stuff from the npm registry.

I mean, it kinda makes sense to have some JS dependencies for a web project, but it was still a bit weird.

Asking in #elixir on IRC gave me the answer though that this was an esbuild watcher that was being started, probably to minify some assets or whatever.

nodejs

OK, esbuild, that's nodejs you might think, there's a variable for that:

export NODE_EXTRA_CA_CERTS="$MY_CA_CERT"

Just that it didn't help, for whatever reason. I didn't feel like debugging why it didn't pick up the variable if there was another way.

As I am writing this, there's still an open issue in this esbuild module for Phoenix, #31.

I used the workaround described there, installing esbuild by hand and then doing

# I do not like install -g
npm install esbuild 
export MIX_ESBUILD_PATH=$(readlink -f node_modules/.bin/esbuild)

but then you need to add this to config/config.exs:

config :esbuild,
  version: "0.14.0",
  path: System.get_env("MIX_ESBUILD_PATH")

But it worked, so it's fine.

erlang/rebar3

Update: Two weeks later I ran into the same problem with erlang's package manager rebar3, but from 3.17 on you can put this:

{ssl_cacerts_path, ["/opt/foo.crt"]}.

into your ~/.config/rebar3/rebar.config and it works.

Docker

Oh, and of course Docker also doesn't work properly behind such a proxy, so I did this, although there should be other solutions:

$ cat /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://proxy.local:8080"
Environment="HTTPS_PROXY=http://proxy.local:8080"
Environment="NO_PROXY=localhost,127.0.0.1,*.localstuff.example.org"

but I've not run into problems without the last line yet, as I don't use a local registry.

You also might need to tweak DNS for Docker:

$ cat /etc/docker/daemon.json
{"dns":["10.0.0.10","9.9.9.9"]}

A quick way to test is running:

$ docker run busybox nslookup example.org

git

And finally there's git, put this into ~/.gitconfig:

[http]
        proxy = http://proxy.local:8080

[https]
        proxy = http://proxy.local:8080

Python/Pip

pip install --cert=/foo/my-cert.crt PACKAGE

Rust/cargo

I've also had problem with Rust's cargo, where solution is supposed to be

[http]
proxy = "proxy.local:8080"
debug = true
cainfo = "/foo/my-cert.crt"

in e.g. ~/.config/cargo.toml (docs) but I didn't get this to work, but it is supposed to fall back to one of these:

CARGO_HTTP_PROXY
HTTPS_PROXY
https_proxy
http_proxy

and that worked, although cargo is unusually slow.

Gradle

For gradle, you need to put these lines into ~/.gradle/gradle.properties:

systemProp.http.proxyHost=proxy.local
systemProp.http.proxyPort=8080
systemProp.https.proxyHost=proxy.local
systemProp.https.proxyPort=8080

Maven

Maven needs this in ~/.m2/settings.xml:

<settings>
  <proxies>
   <proxy>
      <id>http-proxy</id>
      <active>true</active>
      <protocol>http</protocol>
      <host>proxy.local</host>
      <port>8080</port>
      <nonProxyHosts>*.localstuff.example.org|localhost|127.0.0.1</nonProxyHosts>
    </proxy>
   <proxy>
      <id>https-proxy</id>
      <active>true</active>
      <protocol>https</protocol>
      <host>proxy.local</host>
      <port>8080</port>
      <nonProxyHosts>*.localstuff.example.org|localhost|127.0.0.1</nonProxyHosts>
    </proxy>
  </proxies>
</settings>

Java

Finally, for every JVM you use you need to add the cert to the key store, if that's applicable:

For a .crt this is usually done via:

keytool -importcert -alias asdf -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -file cert.crt -noprompt

For GraalVM the path is slightly different:

keytool -importcert -alias asdf -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -file cert.crt -noprompt

And yes, none of this is breaking news, but before I have to put everything together again the next time I'd rather have a consolidated page.

Maybe I'll run into more and make this continually updated, as I had to with the default browser post.