Skip to content

Commit

Permalink
fix detection of private keys with passphrases
Browse files Browse the repository at this point in the history
...and clean up a whole bunch of things; just use stdin for passing the
key, remove atomicSave (not sure why it was needed), and use Go stdlib
for finding home/tmp dirs

Signed-off-by: Alex Suraci <[email protected]>
  • Loading branch information
vito committed Jan 8, 2021
1 parent f2ed56b commit 75d0270
Show file tree
Hide file tree
Showing 12 changed files with 39 additions and 255 deletions.
3 changes: 0 additions & 3 deletions assets/askpass.sh

This file was deleted.

1 change: 0 additions & 1 deletion dockerfiles/alpine/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ RUN pip3 install mercurial
RUN pip3 install hg-evolve

COPY --from=builder /assets /opt/resource
ADD assets/askpass.sh /opt/resource
RUN chmod +x /opt/resource/*
RUN ln -s /opt/resource/hgresource /opt/resource/in; ln -s /opt/resource/hgresource /opt/resource/out; ln -s /opt/resource/hgresource /opt/resource/check
ADD hgrc /etc/mercurial/hgrc
Expand Down
1 change: 0 additions & 1 deletion dockerfiles/ubuntu/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
&& pip2 install hg-evolve

COPY --from=builder /assets /opt/resource
ADD assets/askpass.sh /opt/resource
RUN chmod +x /opt/resource/*
RUN ln -s /opt/resource/hgresource /opt/resource/in; ln -s /opt/resource/hgresource /opt/resource/out; ln -s /opt/resource/hgresource /opt/resource/check
ADD hgrc /etc/mercurial/hgrc
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/nxadm/tail v1.4.6 // indirect
github.com/onsi/ginkgo v1.14.2
github.com/onsi/gomega v1.10.4
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 // indirect
golang.org/x/text v0.3.5 // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U=
github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
Expand Down
14 changes: 6 additions & 8 deletions hgresource/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package main

import (
"fmt"
"github.com/concourse/hg-resource/hg"
"io"
"path"
"os"
"path/filepath"

"github.com/concourse/hg-resource/hg"
)

const cmdCheckName string = "check"
Expand All @@ -17,7 +19,7 @@ var cmdCheck = &Command{

func runCheck(args []string, params *JsonInput, outWriter io.Writer, errWriter io.Writer) int {
repo := hg.Repository{
Path: getCacheDir(),
Path: filepath.Join(os.TempDir(), "hg-resource-repo-cache"),
Branch: params.Source.Branch,
IncludePaths: params.Source.IncludePaths,
ExcludePaths: params.Source.ExcludePaths,
Expand Down Expand Up @@ -49,10 +51,6 @@ func runCheck(args []string, params *JsonInput, outWriter io.Writer, errWriter i
}
}

func getCacheDir() string {
return path.Join(getTempDir(), "hg-resource-repo-cache")
}

func writeLatestCommit(repo *hg.Repository, outWriter io.Writer, errWriter io.Writer) int {
latestCommit, err := repo.GetLatestCommitId()
if err != nil {
Expand All @@ -61,7 +59,7 @@ func writeLatestCommit(repo *hg.Repository, outWriter io.Writer, errWriter io.Wr
}

latestVersion := []Version{
Version{
{
Ref: latestCommit,
},
}
Expand Down
1 change: 1 addition & 0 deletions hgresource/json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"bytes"
"encoding/json"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
Expand Down
1 change: 1 addition & 0 deletions hgresource/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"bytes"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
Expand Down
2 changes: 1 addition & 1 deletion hgresource/out.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ func getTempDirForCommit(commitId string) (string, error) {
return envOverride, nil
}

parentDir := getTempDir()
parentDir := os.TempDir()
prefix := "hg-repo-at-" + commitId
dirForCommit, err := ioutil.TempDir(parentDir, prefix)
if err != nil {
Expand Down
126 changes: 24 additions & 102 deletions hgresource/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,82 +2,76 @@ package main

import (
"bytes"
"crypto/rand"
"encoding/base32"
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"strconv"
"strings"

"golang.org/x/crypto/ssh"
)

const (
tempDirEnv = "TMPDIR"
defaultTempDir = "/tmp"
keyFileName = "hg-resource-private-key"
sshAskpassPath = "/opt/resource/askpass.sh"
sshClientConfig = "StrictHostKeyChecking no\nLogLevel quiet\n"
sshClientConfigFileRelative = ".ssh/config"
)

var ErrPassphraseUnsupported = errors.New("Private keys with passphrases are not supported.")

// Writes SSH private key to a file in $TMPDIR or /tmp, starts ssh-agent and
// loads the key
func loadSshPrivateKey(privateKeyPem string) error {
tempDir := getTempDir()
keyFilePath := path.Join(tempDir, keyFileName)

err := atomicSave(keyFilePath, []byte(privateKeyPem), 0600, 0777)
_, err := ssh.ParsePrivateKey([]byte(privateKeyPem))
if err != nil {
return fmt.Errorf("Error writing private key to disk: %s", err)
var passphraseError *ssh.PassphraseMissingError
if errors.As(err, &passphraseError) {
return ErrPassphraseUnsupported
}

return fmt.Errorf("parse private key: %w", err)
}

err = startSshAgent()
if err != nil {
return err
}

err = addSshKey(keyFilePath)
err = addSshKey(privateKeyPem)
if err != nil {
return err
}

homeDir, err := getHomeDir()
homeDir, err := os.UserHomeDir()
if err != nil {
return err
}

sshClientConfigFile := path.Join(homeDir, sshClientConfigFileRelative)

err = atomicSave(sshClientConfigFile, []byte(sshClientConfig), 0600, 0700)
err = ioutil.WriteFile(sshClientConfigFile, []byte(sshClientConfig), 0600)
if err != nil {
return err
}

return nil
}

func getHomeDir() (string, error) {
homeDir := os.Getenv("HOME")
if len(homeDir) == 0 {
return "", fmt.Errorf("Unable to retrieve home directory from $HOME")
}
return homeDir, nil
}

func addSshKey(keyFilePath string) error {
func addSshKey(privateKeyPem string) error {
stderr := new(bytes.Buffer)
addCmd := exec.Command("ssh-add", keyFilePath)
addCmd.Env = append(addCmd.Env, os.Environ()...)
addCmd.Env = append(addCmd.Env, "DISPLAY=", "SSH_ASKPASS="+sshAskpassPath)
addCmd := exec.Command("ssh-add", "-")
addCmd.Stderr = stderr
addCmd.Stdin = bytes.NewBufferString(privateKeyPem)

err := addCmd.Run()
if err != nil {
errMsg := stderr.String()
if len(errMsg) > 0 {
return fmt.Errorf("Error running ssh-add: %s", errMsg)
return fmt.Errorf("ssh-add: %s", errMsg)
} else {
return fmt.Errorf("Error running ssh-add: %s", err)
return fmt.Errorf("ssh-add: %w", err)
}
}

Expand All @@ -93,7 +87,7 @@ func startSshAgent() error {

err := agentCmd.Run()
if err != nil {
return fmt.Errorf("Error running ssh-agent: %s", err)
return fmt.Errorf("ssh-agent: %w", err)
}

setEnvironmentVariablesFromString(stdout.String())
Expand All @@ -108,7 +102,7 @@ func killSshAgent() error {

pid, err := strconv.Atoi(pidString)
if err != nil {
return fmt.Errorf("Error killing ssh-agent: SSH_AGENT_PID not an integer, but: %s", pidString)
return fmt.Errorf("kill ssh-agent: SSH_AGENT_PID not an integer, but: %s", pidString)
}

proc, err := os.FindProcess(pid)
Expand All @@ -135,75 +129,3 @@ func setEnvironmentVariablesFromString(multiLine string) {
}
}
}

func atomicSave(filename string, content []byte, fileMode os.FileMode, dirMode os.FileMode) error {
dirName := path.Dir(filename)
_, pathErr := os.Stat(dirName)
if pathErr != nil {
err := os.MkdirAll(dirName, dirMode)
if err != nil {
fmt.Errorf("atomicSave(): Error creating directory %s: %s", dirName, err)
}

// mkdir syscall typically doesn't set write flags for all/group
err = os.Chmod(dirName, dirMode)
if err != nil {
fmt.Errorf("atomicSave(): %s", err)
}
}

tempFileName, err := makeTempFileName(filename)
if err != nil {
return err
}

tempFile, err := os.Create(tempFileName)
if err != nil {
return fmt.Errorf("atomicSave(): %s", err)
}

_, err = tempFile.Write(content)
if err != nil {
return fmt.Errorf("atomicSave(): Error writing to file %s: %s", tempFileName, err)
}

err = tempFile.Sync()
if err != nil {
return err
}
err = tempFile.Close()
if err != nil {
return err
}

err = os.Chmod(tempFileName, fileMode)
if err != nil {
return fmt.Errorf("atomicSave(): Error changing permissions of %s: %s", tempFileName, err)
}

err = os.Rename(tempFileName, filename)
if err != nil {
return fmt.Errorf("Error renaming file %s to %s in atomicSave: %s", tempFileName, filename, err)
}

return nil
}

func makeTempFileName(filename string) (string, error) {
randomBytes := make([]byte, 12)
_, err := rand.Read(randomBytes)
if err != nil {
return "", fmt.Errorf("Error creating temporary filename: %s", err)
}

tempFileName := filename + "." + base32.StdEncoding.EncodeToString(randomBytes)
return tempFileName, nil
}

func getTempDir() string {
tempDir := os.Getenv(tempDirEnv)
if len(tempDir) > 0 {
return tempDir
}
return defaultTempDir
}
Loading

0 comments on commit 75d0270

Please sign in to comment.