0

I've set up Git with SSH following https://docs.github.com/en/authentication/connecting-to-github-with-ssh; specifically, I've added my key to the SSH agent following https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#adding-your-ssh-key-to-the-ssh-agent. I'd like to use go-git and have tried the following example (following this comment):

package main

import (
    "log"
    "os"

    "github.com/go-git/go-git/v5"
    "github.com/go-git/go-git/v5/plumbing/transport/ssh"
)

func main() {
    authMethod, err := ssh.DefaultAuthBuilder("keymaster")
    if err != nil {
        log.Fatalf("default auth builder: %v", err)
    }

    if _, err := git.PlainClone("/tmp/foo", false, &git.CloneOptions{
        URL:      "git@github.com:org/repo.git",
        Progress: os.Stdout,
        Auth:     authMethod,
    }); err != nil {
        log.Fatalf("plain clone: %v", err)
    }
}

However, when I run it I get the following error:

> go run main.go
2023/02/20 06:55:25 plain clone: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain
exit status 1

It would appear from https://github.com/go-git/go-git/blob/c35b8082c863f2106de1c3c95ba9ed21d30f9371/plumbing/transport/ssh/common.go#L37-L39 that DefaultAuthBuilder is just an alias for NewSSHAgentAuth, but it is unclear to me how to provide the correct username to obtain the auth method. Does anyone know how to do this?

Kurt Peek
  • 52,165
  • 91
  • 301
  • 526

1 Answers1

1

It looks like the simplest way to get go-git to authenticate using your agent is to let it pick an authentication method by itself. The following seems to work perfectly:

package main

import (
  "log"
  "os"

  "github.com/go-git/go-git/v5"
)

func main() {
  repo := os.Args[1]
  dir := os.Args[2]

  if _, err := git.PlainClone(dir, false, &git.CloneOptions{
    URL:      repo,
    Progress: os.Stdout,
  }); err != nil {
    log.Fatalf("plain clone: %v", err)
  }
}

If I point this at a private repository (./gg git@github.com/larsks/somepriverepo myrepo), it successfully authenticates using a key from my agent and produces a local clone of the repository.


It looks like the username argument to ssh.DefaultAuthBuilder needs to be the target username on the remote system...which in this case is git; so this also works, as long as you're fetching a git@... URL:

package main

import (
  "log"
  "os"

  "github.com/go-git/go-git/v5"
  "github.com/go-git/go-git/v5/plumbing/transport/ssh"
)

func main() {
  repo := os.Args[1]
  dir := os.Args[2]

  authMethod, err := ssh.DefaultAuthBuilder("git")
  if err != nil {
    log.Fatalf("default auth builder: %v", err)
  }

  if _, err := git.PlainClone(dir, false, &git.CloneOptions{
    URL:      repo,
    Progress: os.Stdout,
    Auth:     authMethod,
  }); err != nil {
    log.Fatalf("plain clone: %v", err)
  }
}

For a more general solution you would need to parse the username from the repository URL.

larsks
  • 277,717
  • 41
  • 399
  • 399
  • I've tried this, but I get the same error `plain clone: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain`... – Kurt Peek Feb 20 '23 at 16:22
  • I see the same error as you with your original code, but both of the alternatives presented here run correctly when I test them out locally: https://asciinema.org/a/HgjDf4R2SbZXxam884ZOw9d0W – larsks Feb 20 '23 at 16:24
  • That begs the question why it's working for you and not for me? It seems that your second example is essentially no different from the one in the original question. – Kurt Peek Feb 20 '23 at 16:28
  • There is a crucial difference between the second example and the one in your question: I'm using the correct username in the call to `ssh.DefaultAuthBUilder`. – larsks Feb 20 '23 at 16:29
  • Gotcha, I've copy-pasted the second example and still get the same error; how do you infer the correct username in a general way? – Kurt Peek Feb 20 '23 at 16:31
  • Like I said, you would need to parse it from the repository URL. I guess you would start by identifying `ssh` repository URLs vs others (so either `user@...:repo` or `ssh://user@host/repo`), and then process that appropriately. I would hope that `go-git` has some tooling for parsing git URLs, but I'm not sure. – larsks Feb 20 '23 at 16:41
  • It seems from this example, https://github.com/src-d/go-git/pull/399/files, that one can call `transport.NewEndpoint()` and then access the `User` field of the resulting `*transport.Endpoint`, which in my case is indeed `git` as well; this also jives with the prefix of the git URL as you mention. It seems there is another difference in environment that is causing this to fail in my case. – Kurt Peek Feb 20 '23 at 16:50