Merge pull request #14 from fluxcd/git-auth-helper
Internal helpers for Git auth methods from secrets
This commit is contained in:
commit
f8e0685af1
|
@ -21,16 +21,12 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/go-git/go-git/v5"
|
||||
"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/http"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -40,6 +36,7 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
|
||||
internalgit "github.com/fluxcd/source-controller/internal/git"
|
||||
)
|
||||
|
||||
// GitRepositoryReconciler reconciles a GitRepository object
|
||||
|
@ -78,7 +75,7 @@ func (r *GitRepositoryReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro
|
|||
r.gc(repo)
|
||||
|
||||
// try git clone
|
||||
syncedRepo, err := r.sync(*repo.DeepCopy())
|
||||
syncedRepo, err := r.sync(ctx, *repo.DeepCopy())
|
||||
if err != nil {
|
||||
log.Info("Git repository sync failed", "error", err.Error())
|
||||
}
|
||||
|
@ -103,7 +100,7 @@ func (r *GitRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourcev1.GitRepository, error) {
|
||||
func (r *GitRepositoryReconciler) sync(ctx context.Context, repository sourcev1.GitRepository) (sourcev1.GitRepository, error) {
|
||||
// set defaults: master branch, no tags fetching, max two commits
|
||||
branch := "master"
|
||||
revision := ""
|
||||
|
@ -129,18 +126,29 @@ func (r *GitRepositoryReconciler) sync(repository sourcev1.GitRepository) (sourc
|
|||
}
|
||||
}
|
||||
|
||||
// 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 sourcev1.GitRepositoryNotReady(repository, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
defer os.RemoveAll(tmpSSH)
|
||||
var auth transport.AuthMethod
|
||||
if repository.Spec.SecretRef != nil {
|
||||
name := types.NamespacedName{
|
||||
Namespace: repository.GetNamespace(),
|
||||
Name: repository.Spec.SecretRef.Name,
|
||||
}
|
||||
|
||||
auth, err := r.auth(repository, tmpSSH)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("auth error: %w", err)
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
|
||||
var secret corev1.Secret
|
||||
err := r.Client.Get(ctx, name, &secret)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("auth secret error: %w", err)
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
method, cleanup, err := internalgit.AuthMethodFromSecret(repository.Spec.URL, secret)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("auth error: %w", err)
|
||||
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
|
||||
}
|
||||
if cleanup != nil {
|
||||
defer cleanup()
|
||||
}
|
||||
auth = method
|
||||
}
|
||||
|
||||
// create tmp dir for the Git clone
|
||||
|
@ -321,74 +329,3 @@ func (r *GitRepositoryReconciler) gc(repository sourcev1.GitRepository) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *GitRepositoryReconciler) auth(repository sourcev1.GitRepository, tmp string) (transport.AuthMethod, error) {
|
||||
if repository.Spec.SecretRef == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
name := types.NamespacedName{
|
||||
Namespace: repository.GetNamespace(),
|
||||
Name: repository.Spec.SecretRef.Name,
|
||||
}
|
||||
|
||||
var secret corev1.Secret
|
||||
err := r.Client.Get(context.TODO(), name, &secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
credentials := secret.Data
|
||||
|
||||
// HTTP auth
|
||||
if strings.HasPrefix(repository.Spec.URL, "http") {
|
||||
auth := &http.BasicAuth{}
|
||||
if username, ok := credentials["username"]; ok {
|
||||
auth.Username = string(username)
|
||||
}
|
||||
if password, ok := credentials["password"]; ok {
|
||||
auth.Password = string(password)
|
||||
}
|
||||
|
||||
if auth.Username == "" || auth.Password == "" {
|
||||
return nil, fmt.Errorf("invalid '%s' secret data: required fields username and password",
|
||||
repository.Spec.SecretRef.Name)
|
||||
}
|
||||
|
||||
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("invalid '%s' secret data: required field 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("invalid '%s' secret data: required field 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
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"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/ssh"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func AuthMethodFromSecret(url string, secret corev1.Secret) (transport.AuthMethod, func(), error) {
|
||||
switch {
|
||||
case strings.HasPrefix(url, "http"):
|
||||
auth, err := BasicAuthFromSecret(secret)
|
||||
return auth, nil, err
|
||||
case strings.HasPrefix(url, "ssh"):
|
||||
return PublicKeysFromSecret(secret)
|
||||
}
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func BasicAuthFromSecret(secret corev1.Secret) (*http.BasicAuth, error) {
|
||||
auth := &http.BasicAuth{}
|
||||
if username, ok := secret.Data["username"]; ok {
|
||||
auth.Username = string(username)
|
||||
}
|
||||
if password, ok := secret.Data["password"]; ok {
|
||||
auth.Password = string(password)
|
||||
}
|
||||
if auth.Username == "" || auth.Password == "" {
|
||||
return nil, fmt.Errorf("invalid '%s' secret data: required fields 'username' and 'password'", secret.Name)
|
||||
}
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
func PublicKeysFromSecret(secret corev1.Secret) (*ssh.PublicKeys, func(), error) {
|
||||
identity := secret.Data["identity"]
|
||||
knownHosts := secret.Data["known_hosts"]
|
||||
if len(identity) == 0 || len(knownHosts) == 0 {
|
||||
return nil, nil, fmt.Errorf("invalid '%s' secret data: required fields 'identity' and 'known_hosts'", secret.Name)
|
||||
}
|
||||
|
||||
pk, err := ssh.NewPublicKeys("git", identity, "")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// create tmp dir for known_hosts
|
||||
tmp, err := ioutil.TempDir("", "ssh-"+secret.Name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
cleanup := func() { os.RemoveAll(tmp) }
|
||||
|
||||
knownHostsPath := filepath.Join(tmp, "known_hosts")
|
||||
if err := ioutil.WriteFile(knownHostsPath, knownHosts, 0644); err != nil {
|
||||
cleanup()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
callback, err := ssh.NewKnownHostsCallback(knownHostsPath)
|
||||
if err != nil {
|
||||
cleanup()
|
||||
return nil, nil, err
|
||||
}
|
||||
pk.HostKeyCallback = callback
|
||||
return pk, cleanup, nil
|
||||
}
|
Loading…
Reference in New Issue