GOPROXY(ies) on authentication

Dubo Dubon Duponey
3 min readJul 1, 2021

If you do run your own Athens at any significant scale, there is a good chance you want to provide encryption and/or authentication for it.

The APT world (or more generally the HTTPs world) after all does provide the means to implement mTLS and a a great deal of options for authentication.

Alas, go mod is not yet at that level of maturity, and your options are limited.

Here is what you can, and cannot do.

Using TLS for your server: YES

If you sign with Let’s Encrypt (or any other reputable CA), things will (evidently?) just work.

So called self-signed certificates though, require a little bit more work (as usual).

Here are your options:

Install your CA or server certificate at the OS level

Out of scope here, this is evidently highly dependent on your operating system.

On Debian, you likely want to leverage the ca-certificates package.

On mac, you should be able to get away with it using a machete:

openssl s_client -showcerts -servername go.local -connect go.local:443 </dev/null 2>/dev/null | awk '/BEGIN/,/END/{ if(/BEGIN/){a++}; print}' > go.local.ca.crt
security add-trusted-cert -d -r trustRoot -k ~/Library/Keychains/login.keychain go.local.ca.crt

While feasible, we usually try our best to avoid having to use this approach, specifically because our main use case is in the context of building docker images — for a series of reasons:

  • we certainly do not want our internal CA to ship inside our final images (as there is no reason for the runtime image to trust it), which means you have to be extra careful in how and when you mount it in your image, or how you rely on system tools to handle it (looking at you update-ca-certificates)
  • a system-wide install is likely to have unintended side-effects, specifically… making everything trust this CA — or even trust just this CA, depending on how you do it

Install in a specific location and point to it

That works, for the most part.

Thanks to https://golang.org/src/crypto/x509/root_unix.go, using the SSL_CERT_FILE environment variable allows you to override the system store with your own.

Practically, in a Dockerfile:

ARG           GOPROXY=https://go.local
RUN --mount=type=secret,id=CA \
SSL_CERT_FILE=/run/secrets/CA go mod download

… will now let you use a build secret for your CA, which will not interfere with anything else in your build, will not ship in your final image regardless of how the image is being structured (not everybody does multi-stage unfortunately), is system-independent (*), and requires a minimum amount of boilerplate that does not rely on any system tools.

(*) This will not work natively on macOS: https://github.com/golang/go/issues/37907

Using client TLS certificates / mTLS: NOPE

We use mTLS generally in our internal stack, relying on Caddy to provide TLS termination and TLS client certificate verification for all our services.

Unfortunately, there does not seem to be a way to use client TLS with go tools right now: https://github.com/golang/go/issues/30119

Using basic authentication: YES

Short of being able to use mTLS, you possibly want to fallback on credentials based authentication.

go tools do support netrc files to pass credentials for specific servers.

The format of netrc is widely documented (if tribally). In a shell:

machine go.local login dubo-dubon-duponey password XXXXXXXXXX

… which you can use in a similar fashion as above in a Dockerfile (be SURE to use a secret for this one, evidently):

ARG           GOPROXY=https://go.local
RUN --mount=type=secret,id=CA \
--mount=type=secret,id=NETRC \
NETRC=/run/secrets/NETRC SSL_CERT_FILE=/run/secrets/CA go mod download

This works fine, for the most part, but…

Using OAuth: NOPE

Unfortunately, and unlike libcurl, other means of authentication besides basic (eg: OAuth) cannot be used currently: https://github.com/golang/go/issues/40215

Indeed, empty passwords or logins are not handled at all: https://github.com/golang/go/blob/fa98f46741f818913a8c11b877520a548715131f/src/cmd/go/internal/auth/netrc.go#L65

Take-away

GOPROXY and modules are still fairly new in the go world, and obviously require more work to provide the flexibility that other (older) systems offer (apt).

Still, you can use (self-signed or not) TLS for your proxy, and basic authentication.

Though, and short of relying on more arcane stuff (eg: mitmdump), you are out of luck for mTLS unfortunately. Or any form of not-basic authentication.

--

--