How to use private repositories with Go modules

July 1, 2020 · 2 minute read

Since the release of 1.13, Go has native Go Modules support, with go mod. Similar to other tooling in the Go ecosystem, like go fmt for code formatting, it is intended to be the default, community-concended approach to dependency management. It supersedes existing approaches, such as Glide and Dep, and integrates smoothly within the existing Go workflow.

It works out of the box, without any additional configuration.

However, that is only true when all dependencies are publicly available. With dependencies on private packages, additional configuration is needed.

Resolving dependencies over SSH instead of HTTPS

Without any additional steps, trying to load a private dependency into the Go Modules project, will result in the following error:

go: downloading github.com/myorg/privaterepo v0.0.0-20200408100711-ed766a2975ce
go get github.com/myorg/privaterepo: github.com/myorg/[email protected]: verifying module: github.com/myorg/[email protected]: reading https://sum.golang.org/lookup/github.com/myorg/[email protected]: 410 Gone
        server response:
        not found: github.com/myorg/[email protected]: invalid version: git fetch -f origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /tmp/gopath/pkg/mod/cache/vcs/13e63a509893edc19353a80fa2c6e28db213d146f72fe43ba65c1ec86624027b: exit status 128:
                fatal: could not read Username for 'https://github.com': terminal prompts disabled

The problem here is that, by default, Go’s dependency resolving approach attempts to download the package with git over HTTPS.

We want to (instead of using HTTPS) download the private dependency over SSH, such that we can use the local SSH keys to gain access to the package repository. This assumes that you have configured SSH with credentials that can access the private repositories. If you have not, Github has an excellent tutorial how to set it up.

Because this is not something we can change in Go, what we can do is to configure git to rewrite all HTTPs to the equivalent git URLs:

git config --add --global url."[email protected]:".insteadOf https://github.com
git config --add --global url."[email protected]:".insteadOf https://bitbucket.org
git config --add --global url."[email protected]:".insteadOf https://gitlab.com

By-passing GOPROXY for private dependencies

Although this has been resolved in the newer versions of Go, in earlier versions resolving the dependencies over SSH still does not completely solve the issue. When we try to download the private dependency, with the SSH patch, the following error will occur:

go: github.com/myorg/[email protected]: reading https://proxy.golang.org/github.com/myorg/privaterepo/@v/v0.0.0-20200512204819-655dcc74320a.mod: 410 Gone

This error occurs due to the introduction of a new default for GOPROXY in Go 1.13. From that version on, Go uses a module mirror (https://proxy.golang.org) to resolve the modules. This mirror obviously will not contain or be able to find the private repositories.

To fix this, there are a couple of options:

GOPROXY A crude solution to resolve this issue is to simply skip the proxy. This will avoid Go to hit the intermediate proxy servers completely and directly contact Github. To do this, set:

export GOPROXY=direct

GOPRIVATE Another option is to specifically mark packages as private by adding them to GOPRIVATE. You can either mark specific packages:

export GOPRIVATE=github.com/myorg/privaterepo

Alternatively, you could mark all packages in your organization as private:

export GOPRIVATE=github.com/myorg

To permanently use this fix, you can either export these variables in your shell start-up scripts, or you can override the defaults of Go:

go env -w 

To verify that the changes have persisted, you can see the environment used for Go commands with:

go env | grep 'GOPROXY\|GOPRIVATE'

Conclusion

With the fixes in place you should be able to access all private repositories that you can accessible with your loaded SSH credentials. In case, you want to use HTTPS or cannot rely on SSH, there are alternative (in my opinion, more cumbersome) documented approaches that rely on Github access tokens.

Further reading

Tags:

Updated: