Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/go: go get fails for long paths #30623

Closed
palsivertsen opened this issue Mar 6, 2019 · 6 comments

Comments

@palsivertsen
Copy link

commented Mar 6, 2019

tl;dr

  • Having dependencies behind an auth scheme is not possible and/or workarounds are not practical/usable for developers and build pipelines.
  • Go toolchain does not understand urls with more than two path items

Background

I have a private/hidden project hosted on GitLab where the URL looks something like this:
gitlab.com/my-company/my-project/my-subproject/my-important-library
Since it's a private repository, there's a need to authenticate. The simplest way is to use SSH with a key pair, as it avoids user interaction. However the go toolchain insists on using HTTPS, possibly due to the way gitlab handles go-get=1 requests. The docs suggest two approaches to get around this:

  • HTTPS: authenticate using credentials from .netrc
  • Force SSH: rewrite url to trick git into using ssh instead of https

None of these workarounds seems to work. Aside from that, there are some problems:
Using .netrc uses clear text passwords/tokens and it's not possible to use several credentials per host. Using .gitconfig globally does not allow several credentials per host. Configuring it locally for each project is a possibility. All of these approaches get sort of hidden away and are not very intuitive for anyone who needs to use my-important-library. It's also a nightmare to setup a build server.

Having lost the ssh fight with git/go get/go mod I tried adding GIT_TERMINAL_PROMPT=1 to my commands like so:
GIT_TERMINAL_PROMPT=1 go get gitlab.com/my-company/my-project/my-subproject/my-important-library
GIT_TERMINAL_PROMPT=1 go mod download

This introduced me to a different problem. Seems like go can only understand urls in the form <host>/<user>/<library>.

What did you do?

$ GIT_TERMINAL_PROMPT=1 go get gitlab.com/my-company/my-project/my-subproject/my-important-library

What did you expect to see?

EXIT_CODE=0 and my-important-library on disk

What did you see instead?

$ GIT_TERMINAL_PROMPT=1 go get gitlab.com/my-company/my-project/my-subproject/my-important-library
Username for 'https://gitlab.com': palsivertsen
Password for 'https://palsivertsen@gitlab.com': 
# cd .; git clone https://gitlab.com/my-company/my-project.git /home/pal/projects/go/src/gitlab.com/my-company/my-project
Cloning into '/home/pal/projects/go/src/gitlab.com/my-company/my-project'...
remote: The project you were looking for could not be found.
fatal: repository 'https://gitlab.com/my-company/my-project.git/' not found
package gitlab.com/my-company/my-project/my-subproject/my-important-library: exit status 128

Note that the half the url (/my-subproject/my-important-library) is missing.

System details

go version go1.12 linux/amd64
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/pal/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/pal/projects/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
GOROOT/bin/go version: go version go1.12 linux/amd64
GOROOT/bin/go tool compile -V: compile version go1.12
uname -sr: Linux 4.15.0-45-generic
Distributor ID:	Ubuntu
Description:	Ubuntu 16.04.6 LTS
Release:	16.04
Codename:	xenial
/lib/x86_64-linux-gnu/libc.so.6: GNU C Library (Ubuntu GLIBC 2.23-0ubuntu11) stable release version 2.23, by Roland McGrath et al.
gdb --version: GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
$ git version
git version 2.7.4
@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Mar 6, 2019

From Jan Mercl at https://groups.google.com/forum/?pli=1#!topic/golang-nuts/83hUVBGu7rA :

Seems like your git repo is just misconfigured. go get does not actually download things, it calls out git or other VCS to do that. I'm also using gitlab for private repositories and have no issues with go get.

Example https (wrong) config:

==== jnml@4670:~/src/modernc.org/ql> cat .git/config 
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
[remote "origin"]
	url = https://gitlab.com/cznic/ql
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/master
==== jnml@4670:~/src/modernc.org/ql> 

Make sure the url item has the right (git) protocol. Example correct config:

==== jnml@4670:~/src/modernc.org/cc> cat .git/config 
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
[remote "origin"]
	url = git@gitlab.com:cznic/cc
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/master
[branch "gccgo"]
	remote = origin
	merge = refs/heads/gccgo
[branch "feb"]
	remote = origin
	merge = refs/heads/feb
==== jnml@4670:~/src/modernc.org/cc>

Above assumes you have your keys correctly registered at gitlab.

@andybons andybons changed the title Go get fails for long paths cmd/go: go get fails for long paths Mar 9, 2019

@andybons andybons added this to the Unplanned milestone Mar 9, 2019

@palsivertsen

This comment has been minimized.

Copy link
Author

commented Mar 19, 2019

Seems like your git repo is just misconfigured.

It looks like you're referring to git remote configuration. I don't think that affects how go get will fetch dependencies?

The workaround

I've been able to get it working, but it's very unintuitive and will force all developers to use ssh transport.

My goal is to fetch a go module over ssh located at gitlab.com/my-company/my-project/my-subproject/my-important-library

The first thing I needed to do was to rename the go module from gitlab.com/my-company/my-project/my-subproject/my-important-library to gitlab.com/my-company/my-project/my-subproject/my-important-library.git (add .git ending).

Then I updated my global .gitconfig to rewrite all gitlab requests over HTTPS like so:

git config --global url."git@gitlab.com:".insteadOf https://gitlab.com/

Lastly I added .git postfix to all my import paths:

import (
  "gitlab.com/my-company/my-project/my-subproject/my-important-library.git"
  "gitlab.com/my-company/my-project/my-subproject/my-important-library.git/foo"
  "gitlab.com/my-company/my-project/my-subproject/my-important-library.git/bar"
)

Problems

  • The name of the module needs to change (.git postfix in go.mod). This breaks all existing usage of the module.
  • Changing the global git config in this way might cause unexpected behaviour later on.
  • This gitconfig, or .netrc equivalent, hack needs to be done on every machine that deals with code that uses this dependency (developers, build systems, etc).
  • The import path changes, even though the actual location does not.
  • Importing alongside public libraries with similar path looks weird:
import (
  "gitlab.com/my-company/my-project/my-subproject/my-important-library.git/foo"
  "gitlab.com/my-company/my-project/my-subproject/my-public-library/foo" // <- No ".git" postfix!
)

Solutions

#30304 proposes some solutions to this problem (Idea 1 (add a query param for selecting a transport) and Idea 2 (let the server return multiple go-import URLs)), but I don't think depending on changes to server implementations is the way to go here.

Proposal 1 - Make go tool chain smarter in how it selects transport for downloading dependencies

Automatically try ssh if git clone(?) over https fails. The working protocol could be cached for future requests.

Proposal 2 - Override dependency transport for the go tool chain

Introduce a way to override the transport used for different dependencies.

Example: Tool chain looks for a file named go.transport (similar to go.mod):

ssh (
  gitlab.com/my-company/my-project/my-subproject/my-important-library
)

If this file is outside the project path or not in VCS, then each developer can override this however they like.

It is important that this is configured in the go tool chain and not in git, so that we don't mess up the git tool itself.

@bcmills

This comment has been minimized.

Copy link
Member

commented Apr 16, 2019

Proposal 1 - Make go tool chain smarter in how it selects transport for downloading dependencies

That is exactly #30304, which you've already found. (Note the resolution in #30304 (comment).)

Proposal 2 - Override dependency transport for the go tool chain

That should not be necessary once #29888 is addressed.

@bcmills

This comment has been minimized.

Copy link
Member

commented Apr 16, 2019

To address some specific points:

My goal is to fetch a go module over ssh located at gitlab.com/my-company/my-project/my-subproject/my-important-library

Why force ssh instead of fixing HTTPS?

  • Changing the global git config [to use https instead of ssh] might cause unexpected behaviour later on.

Any change to the go command might cause unexpected behavior. If there is a specific reason for concern with regard to git and insteadOf, please elaborate.

  • This gitconfig, or .netrc equivalent, hack needs to be done on every machine that deals with code that uses this dependency (developers, build systems, etc).

Don't you need to configure every machine that needs access to a private repo with credentials anyway?

@palsivertsen

This comment has been minimized.

Copy link
Author

commented Apr 21, 2019

That is exactly #30304, which you've already found. (Note the resolution in #30304 (comment).)

No, in #30304 both proposals rely on the server to tell go which transport to use. Proposal 1 is a fallback solution and does not rely on the server to select a protocol.

Why force ssh instead of fixing HTTPS?

See 26232 comment

If there is a specific reason for concern with regard to git and insteadOf, please elaborate.

I'm not sure if this will be an issue, but if one configure git with url."git@gitlab.com:".insteadOf https://gitlab.com/ it will affect the whole domain.

Don't you need to configure every machine that needs access to a private repo with credentials anyway?

Yes, I guess you're right.


It just feels wrong to setup (in my case) additional credentials for go when git is happy to work with the credentials already in place.

@bcmills bcmills removed the WaitingForInfo label Apr 30, 2019

@bcmills

This comment has been minimized.

Copy link
Member

commented May 7, 2019

I think the rationale in #30304 (comment) still applies here.

Why force ssh instead of fixing HTTPS?

See 26232 comment

In that comment, you state:

Making HTTP calls that requires authentication is (IMO) a poor choice compared to SSH because it requires either user interaction (2FA) or weak credentials (compared to SSH). Another argument to drop HTTP is that SSH credentials are in most cases only authorized for GIT, but HTTP credentials in some cases gives access to a wider API.

I don't see why either of those is intrinsically the case. GitHub “personal access tokens” can be authorized for repo access only. SSH keys either require a password (that is, a user interaction similar to that required by 2FA), or provide a level of security similar to a passwordless SSH key (if someone has access to your machine's SSH private key, that's just as bad as someone who has access to your machine's HTTPS access token).

Given that the practical difference is very slight, it doesn't seem worthwhile to add extra complexity to the go command or its protocols to address.

@bcmills bcmills closed this May 7, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.