From 1ea4c0b609284190d3302b7627b1f0e7ce37d38e Mon Sep 17 00:00:00 2001 From: ericb-summit <75135427+ericb-summit@users.noreply.github.com> Date: Fri, 14 Oct 2022 09:50:26 -0400 Subject: [PATCH] Support SSH public key authentication for submodules (#102) --- README.md | 1 + pkg/in/agent.go | 50 +++++++++++++++++++++++++++++++++++++++++++++++ pkg/in/command.go | 19 ++++++++++++++++++ pkg/models.go | 1 + 4 files changed, 71 insertions(+) create mode 100644 pkg/in/agent.go diff --git a/README.md b/README.md index 1165fdf..78c49c9 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ resources: * `target_branch`(string): Filter merge requests by target_branch. Default is empty string. * `source_branch`(string): Filter merge requests by source_branch. Default is empty string. * `sort` (string): Merge requests sorting order, either `asc` (default) or `desc` to reverse. +* `ssh_keys` (string[]): When set to a non-empty array, an ssh-agent will be started and the specified keys will be added to it. This is only relevant for submodules with an ssh URL and passphrase encrypted keys are not supported. * `recursive`: When set to `true`, will pull submodules by issuing a `git submodule update --init --recursive`. Note that if your submodules are hosted on the same server, be sure to [use a relative path](https://www.gniibe.org/memo/software/git/using-submodule.html) to avoid ssh/https protocol clashing (as the MR is fetched via https, this resource would have no way to authenticate a git+ssh connection). ## Behavior diff --git a/pkg/in/agent.go b/pkg/in/agent.go new file mode 100644 index 0000000..aab0fde --- /dev/null +++ b/pkg/in/agent.go @@ -0,0 +1,50 @@ +package in + +import ( + "os" + "os/exec" + "strings" +) + +type AgentRunner interface { + Start() error + AddKey(key string) error +} + +func NewAgentRunner() AgentRunner { + return &AgentRunnerImpl{ + sockPath: "/tmp/ssh-agent.sock", + } +} + +type AgentRunnerImpl struct { + sockPath string + agent *exec.Cmd +} + +func (r *AgentRunnerImpl) Start() error { + if r.agent != nil { + return nil // already running + } + agent := exec.Command("ssh-agent", "-a", r.sockPath) + agent.Stdin = os.Stdin + agent.Stderr = os.Stderr + err := agent.Run() + if err != nil { + return err + } + r.agent = agent + os.Setenv("SSH_AUTH_SOCK", r.sockPath) + return nil +} + +func (r AgentRunnerImpl) AddKey(key string) error { + command := exec.Command("ssh-add", "-") + command.Stderr = os.Stderr + command.Stdin = strings.NewReader(key + "\n") // this trailing newline is mandatory or ssh-add will fail + err := command.Run() + if err != nil { + return err + } + return nil +} diff --git a/pkg/in/command.go b/pkg/in/command.go index e90e8d7..f12f3f3 100644 --- a/pkg/in/command.go +++ b/pkg/in/command.go @@ -12,12 +12,14 @@ import ( type Command struct { client *gitlab.Client runner GitRunner + agent AgentRunner } func NewCommand(client *gitlab.Client) *Command { return &Command{ client, NewRunner(), + NewAgentRunner(), } } @@ -69,6 +71,23 @@ func (command *Command) Run(destination string, request Request) (Response, erro return Response{}, err } + if (request.Source.SshKeys != nil) && (len(request.Source.SshKeys) != 0) { + err = command.runner.Run("config", "--global", "core.sshCommand", "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no") + if err != nil { + return Response{}, err + } + err = command.agent.Start() + if err != nil { + return Response{}, err + } + for _, key := range request.Source.SshKeys { + err = command.agent.AddKey(key) + if err != nil { + return Response{}, err + } + } + } + os.Chdir(destination) err = command.runner.Run("remote", "add", "source", source.String()) diff --git a/pkg/models.go b/pkg/models.go index 0c5d044..5f6c05c 100644 --- a/pkg/models.go +++ b/pkg/models.go @@ -26,6 +26,7 @@ type Source struct { Sort string `json:"sort,omitempty"` Paths []string `json:"paths,omitempty"` IgnorePaths []string `json:"ignore_paths,omitempty"` + SshKeys []string `json:"ssh_keys,omitempty"` } type Version struct {