Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 26 additions & 15 deletions internal/git/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
gitssh "github.com/go-git/go-git/v6/plumbing/transport/ssh"
"github.com/prometheus/client_golang/prometheus"
gossh "golang.org/x/crypto/ssh"
"sigs.k8s.io/controller-runtime/pkg/log"
)

const (
Expand Down Expand Up @@ -54,7 +55,7 @@ func (m *managerImpl) CloneRepository(ctx context.Context, repoUrl, subPath, ref
return nil, fmt.Errorf("failed to create temporary directory: %w", err)
}

clientOpts, tempFile, err := m.getClientOptions(parsedURL.Scheme, auth)
clientOpts, tempFile, err := m.getClientOptions(ctx, parsedURL.Scheme, auth)
if err != nil {
return nil, fmt.Errorf("failed to configure auth: %w", err)
}
Expand Down Expand Up @@ -89,9 +90,9 @@ func (m *managerImpl) CloneRepository(ctx context.Context, repoUrl, subPath, ref
}, nil
}

func (m *managerImpl) getClientOptions(scheme string, authSecret map[string][]byte) ([]client.Option, string, error) {
func (m *managerImpl) getClientOptions(ctx context.Context, scheme string, authSecret map[string][]byte) ([]client.Option, string, error) {
if scheme == "ssh" {
return m.getSSHClientOptions(authSecret)
return m.getSSHClientOptions(ctx, authSecret)
}
opts := m.getHTTPClientOptions(authSecret)
return opts, "", nil
Expand Down Expand Up @@ -137,9 +138,12 @@ func ensureKnownHostsExists() error {
return nil
}

func (m *managerImpl) getSSHClientOptions(authSecret map[string][]byte) ([]client.Option, string, error) {
func (m *managerImpl) getSSHClientOptions(ctx context.Context, authSecret map[string][]byte) ([]client.Option, string, error) {
logger := log.FromContext(ctx)

privateKey, hasKey := authSecret["sshPrivateKey"]
if !hasKey {
logger.Info("SSH host key verification is disabled, no SSH credentials provided in auth secret")
return []client.Option{
client.WithSSHAuth(&gitssh.Password{
User: "git",
Expand All @@ -153,21 +157,28 @@ func (m *managerImpl) getSSHClientOptions(authSecret map[string][]byte) ([]clien
if err != nil {
return nil, "", fmt.Errorf("failed to parse SSH private key: %w", err)
}
auth.HostKeyCallback = gossh.InsecureIgnoreHostKey()

var tempFilePath string
if knownHostsData, ok := authSecret["known_hosts"]; ok {
tmpFile, err := os.CreateTemp("", "known_hosts-*")
if err == nil {
if _, err := tmpFile.Write(knownHostsData); err == nil {
_ = tmpFile.Close()
tempFilePath = tmpFile.Name()
cb, err := gitssh.NewKnownHostsCallback(tempFilePath)
if err == nil {
auth.HostKeyCallback = cb
}
}
if err != nil {
return nil, "", fmt.Errorf("failed to create known_hosts temp file: %w", err)
}
if _, err := tmpFile.Write(knownHostsData); err != nil {
_ = tmpFile.Close()
_ = os.Remove(tmpFile.Name())
return nil, "", fmt.Errorf("failed to write known_hosts temp file: %w", err)
}
_ = tmpFile.Close()
tempFilePath = tmpFile.Name()
cb, err := gitssh.NewKnownHostsCallback(tempFilePath)
if err != nil {
_ = os.Remove(tempFilePath)
return nil, "", fmt.Errorf("failed to configure known_hosts callback: %w", err)
}
auth.HostKeyCallback = cb
} else {
logger.Info("SSH host key verification is disabled, provide known_hosts in auth secret to enable verification")
auth.HostKeyCallback = gossh.InsecureIgnoreHostKey()
}

return []client.Option{client.WithSSHAuth(auth)}, tempFilePath, nil
Expand Down
35 changes: 28 additions & 7 deletions internal/git/manager_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package git

import (
"context"
"os"
"testing"

"github.com/go-git/go-git/v6/plumbing/transport"
Expand All @@ -20,7 +22,7 @@ JtdGRlLmNzYgECAw==
func TestGetClientOptions_HTTPToken(t *testing.T) {
m := &managerImpl{}
secret := map[string][]byte{"token": []byte("my-token")}
opts, tmpFile, err := m.getClientOptions("https", secret)
opts, tmpFile, err := m.getClientOptions(context.Background(), "https", secret)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand All @@ -35,7 +37,7 @@ func TestGetClientOptions_HTTPToken(t *testing.T) {
func TestGetClientOptions_HTTPUsernamePassword(t *testing.T) {
m := &managerImpl{}
secret := map[string][]byte{"username": []byte("user"), "password": []byte("pass")}
opts, _, err := m.getClientOptions("http", secret)
opts, _, err := m.getClientOptions(context.Background(), "http", secret)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand All @@ -46,7 +48,7 @@ func TestGetClientOptions_HTTPUsernamePassword(t *testing.T) {

func TestGetClientOptions_HTTPEmpty(t *testing.T) {
m := &managerImpl{}
opts, _, err := m.getClientOptions("https", nil)
opts, _, err := m.getClientOptions(context.Background(), "https", nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand All @@ -57,7 +59,7 @@ func TestGetClientOptions_HTTPEmpty(t *testing.T) {

func TestGetClientOptions_SSHNoSecret(t *testing.T) {
m := &managerImpl{}
opts, _, err := m.getClientOptions(sshScheme, nil)
opts, _, err := m.getClientOptions(context.Background(), sshScheme, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand All @@ -68,7 +70,7 @@ func TestGetClientOptions_SSHNoSecret(t *testing.T) {

func TestGetClientOptions_SSHEmptySecret(t *testing.T) {
m := &managerImpl{}
opts, _, err := m.getClientOptions(sshScheme, map[string][]byte{})
opts, _, err := m.getClientOptions(context.Background(), sshScheme, map[string][]byte{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand All @@ -80,7 +82,7 @@ func TestGetClientOptions_SSHEmptySecret(t *testing.T) {
func TestGetClientOptions_SSHWithPrivateKey(t *testing.T) {
m := &managerImpl{}
secret := map[string][]byte{"sshPrivateKey": []byte(testEd25519PrivateKey)}
opts, _, err := m.getClientOptions(sshScheme, secret)
opts, _, err := m.getClientOptions(context.Background(), sshScheme, secret)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand All @@ -89,10 +91,29 @@ func TestGetClientOptions_SSHWithPrivateKey(t *testing.T) {
}
}

func TestGetClientOptions_SSHWithPrivateKeyAndKnownHosts(t *testing.T) {
m := &managerImpl{}
secret := map[string][]byte{
"sshPrivateKey": []byte(testEd25519PrivateKey),
"known_hosts": []byte("github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl\n"),
}
opts, tmpFile, err := m.getClientOptions(context.Background(), sshScheme, secret)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(opts) != 1 {
t.Fatalf("expected 1 option, got %d", len(opts))
}
if tmpFile == "" {
t.Fatal("expected temp known_hosts file path, got empty string")
}
defer os.Remove(tmpFile)
}

func TestGetClientOptions_SSHWithInvalidKey(t *testing.T) {
m := &managerImpl{}
secret := map[string][]byte{"sshPrivateKey": []byte("not-a-valid-key")}
_, _, err := m.getClientOptions(sshScheme, secret)
_, _, err := m.getClientOptions(context.Background(), sshScheme, secret)
if err == nil {
t.Fatal("expected error for invalid SSH key, got nil")
}
Expand Down
Loading