Implement Git SSH authentication

This commit is contained in:
stefanprodan 2020-04-11 21:30:39 +03:00
parent bbc25c3de0
commit 06af12739d
1 changed files with 53 additions and 10 deletions

View File

@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"strings" "strings"
"time" "time"
@ -29,6 +30,7 @@ import (
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
"github.com/go-logr/logr" "github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -153,22 +155,30 @@ func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourc
} }
} }
auth, err := r.auth(repository) // create tmp dir for SSH known_hosts
tmpSSH, err := ioutil.TempDir("", repository.Name)
if err != nil {
err = fmt.Errorf("tmp dir error %w", err)
return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err
}
defer os.RemoveAll(tmpSSH)
auth, err := r.auth(repository, tmpSSH)
if err != nil { if err != nil {
err = fmt.Errorf("auth error %w", err) err = fmt.Errorf("auth error %w", err)
return NotReadyCondition(sourcev1.AuthenticationFailedReason, err.Error()), "", err return NotReadyCondition(sourcev1.AuthenticationFailedReason, err.Error()), "", err
} }
// create tmp dir // create tmp dir for the Git clone
dir, err := ioutil.TempDir("", repository.Name) tmpGit, err := ioutil.TempDir("", repository.Name)
if err != nil { if err != nil {
err = fmt.Errorf("tmp dir error %w", err) err = fmt.Errorf("tmp dir error %w", err)
return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err
} }
defer os.RemoveAll(dir) defer os.RemoveAll(tmpGit)
// clone to tmp // clone to tmp
repo, err := git.PlainClone(dir, false, &git.CloneOptions{ repo, err := git.PlainClone(tmpGit, false, &git.CloneOptions{
URL: repository.Spec.URL, URL: repository.Spec.URL,
Auth: auth, Auth: auth,
RemoteName: "origin", RemoteName: "origin",
@ -256,6 +266,7 @@ func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourc
} }
} }
} }
// read commit hash // read commit hash
ref, err := repo.Head() ref, err := repo.Head()
if err != nil { if err != nil {
@ -282,7 +293,7 @@ func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourc
defer unlock() defer unlock()
// archive artifact // archive artifact
err = r.Storage.Archive(artifact, dir, "") err = r.Storage.Archive(artifact, tmpGit, "")
if err != nil { if err != nil {
err = fmt.Errorf("storage error %w", err) err = fmt.Errorf("storage error %w", err)
return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err return NotReadyCondition(sourcev1.StorageOperationFailedReason, err.Error()), "", err
@ -329,7 +340,7 @@ func (r *GitRepositoryReconciler) gc(repository sourcev1.GitRepository) {
} }
} }
func (r *GitRepositoryReconciler) auth(repository sourcev1.GitRepository) (transport.AuthMethod, error) { func (r *GitRepositoryReconciler) auth(repository sourcev1.GitRepository, tmp string) (transport.AuthMethod, error) {
if repository.Spec.SecretRef == nil { if repository.Spec.SecretRef == nil {
return nil, nil return nil, nil
} }
@ -347,21 +358,53 @@ func (r *GitRepositoryReconciler) auth(repository sourcev1.GitRepository) (trans
credentials := secret.Data credentials := secret.Data
// extract HTTP credentials // HTTP auth
if strings.HasPrefix(repository.Spec.URL, "http") { if strings.HasPrefix(repository.Spec.URL, "http") {
auth := &http.BasicAuth{} auth := &http.BasicAuth{}
if username, ok := credentials["username"]; ok { if username, ok := credentials["username"]; ok {
auth.Username = string(username) auth.Username = string(username)
} else { } else {
return nil, fmt.Errorf("%s secret does not contain a username", repository.Spec.SecretRef.Name) return nil, fmt.Errorf("%s secret does not contain username", repository.Spec.SecretRef.Name)
} }
if password, ok := credentials["password"]; ok { if password, ok := credentials["password"]; ok {
auth.Password = string(password) auth.Password = string(password)
} else { } else {
return nil, fmt.Errorf("%s secret does not contain a password", repository.Spec.SecretRef.Name) return nil, fmt.Errorf("%s secret does not contain password", repository.Spec.SecretRef.Name)
} }
return auth, nil return auth, nil
} }
// SSH auth
if strings.HasPrefix(repository.Spec.URL, "ssh") {
var privateKey []byte
if identity, ok := credentials["identity"]; ok {
privateKey = identity
} else {
return nil, fmt.Errorf("%s secret does not contain identity", repository.Spec.SecretRef.Name)
}
pk, err := ssh.NewPublicKeys("git", privateKey, "")
if err != nil {
return nil, err
}
known_hosts := filepath.Join(tmp, "known_hosts")
if kh, ok := credentials["known_hosts"]; ok {
if err := ioutil.WriteFile(filepath.Join(tmp, "known_hosts"), kh, 0644); err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("%s secret does not contain known_hosts", repository.Spec.SecretRef.Name)
}
callback, err := ssh.NewKnownHostsCallback(known_hosts)
if err != nil {
return nil, err
}
pk.HostKeyCallback = callback
return pk, nil
}
return nil, nil return nil, nil
} }