Skip to content

Commit 67b10aa

Browse files
committed
Respect configured user in SSH Git repository URL
We had a hardcoded assumption that the SSH user for a Git repository is always "git". This is however not true in all scenarios, for example when one is making use of Gerrit for team code collaboration, as users there have their own username for (SSH) Git operations. This commit changes the logic of the auth strategy helpers to: 1. Select the auth strategy based on the protocol of the parsed URL, instead of a simple rely on a correct prefix. 2. Use the user information from the parsed URL to configure the user for the public key authentication strategy, with a fallback to `git` if none is defined. Signed-off-by: Hidde Beydals <[email protected]>
1 parent 1ea9999 commit 67b10aa

File tree

3 files changed

+45
-21
lines changed

3 files changed

+45
-21
lines changed

controllers/gitrepository_controller.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,15 +184,19 @@ func (r *GitRepositoryReconciler) reconcile(ctx context.Context, repository sour
184184

185185
// determine auth method
186186
var auth transport.AuthMethod
187-
authStrategy := git.AuthSecretStrategyForURL(repository.Spec.URL)
188-
if repository.Spec.SecretRef != nil && authStrategy != nil {
187+
if repository.Spec.SecretRef != nil {
188+
authStrategy, err := git.AuthSecretStrategyForURL(repository.Spec.URL)
189+
if err != nil {
190+
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err
191+
}
192+
189193
name := types.NamespacedName{
190194
Namespace: repository.GetNamespace(),
191195
Name: repository.Spec.SecretRef.Name,
192196
}
193197

194198
var secret corev1.Secret
195-
err := r.Client.Get(ctx, name, &secret)
199+
err = r.Client.Get(ctx, name, &secret)
196200
if err != nil {
197201
err = fmt.Errorf("auth secret error: %w", err)
198202
return sourcev1.GitRepositoryNotReady(repository, sourcev1.AuthenticationFailedReason, err.Error()), err

pkg/git/transport.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package git
1818

1919
import (
2020
"fmt"
21-
"strings"
21+
"net/url"
2222

2323
"github.com/go-git/go-git/v5/plumbing/transport"
2424
"github.com/go-git/go-git/v5/plumbing/transport/http"
@@ -28,14 +28,21 @@ import (
2828
"github.com/fluxcd/pkg/ssh/knownhosts"
2929
)
3030

31-
func AuthSecretStrategyForURL(url string) AuthSecretStrategy {
31+
const defaultPublicKeyAuthUser = "git"
32+
33+
func AuthSecretStrategyForURL(URL string) (AuthSecretStrategy, error) {
34+
u, err := url.Parse(URL)
35+
if err != nil {
36+
return nil, fmt.Errorf("failed to parse URL to determine auth strategy: %w", err)
37+
}
3238
switch {
33-
case strings.HasPrefix(url, "http"):
34-
return &BasicAuth{}
35-
case strings.HasPrefix(url, "ssh"):
36-
return &PublicKeyAuth{}
39+
case u.Scheme == "http", u.Scheme == "https":
40+
return &BasicAuth{}, nil
41+
case u.Scheme == "ssh":
42+
return &PublicKeyAuth{user: u.User.Username()}, nil
43+
default:
44+
return nil, fmt.Errorf("no auth secret strategy for scheme %s", u.Scheme)
3745
}
38-
return nil
3946
}
4047

4148
type AuthSecretStrategy interface {
@@ -58,7 +65,9 @@ func (s *BasicAuth) Method(secret corev1.Secret) (transport.AuthMethod, error) {
5865
return auth, nil
5966
}
6067

61-
type PublicKeyAuth struct{}
68+
type PublicKeyAuth struct {
69+
user string
70+
}
6271

6372
func (s *PublicKeyAuth) Method(secret corev1.Secret) (transport.AuthMethod, error) {
6473
identity := secret.Data["identity"]
@@ -67,7 +76,12 @@ func (s *PublicKeyAuth) Method(secret corev1.Secret) (transport.AuthMethod, erro
6776
return nil, fmt.Errorf("invalid '%s' secret data: required fields 'identity' and 'known_hosts'", secret.Name)
6877
}
6978

70-
pk, err := ssh.NewPublicKeys("git", identity, "")
79+
user := s.user
80+
if user == "" {
81+
user = defaultPublicKeyAuthUser
82+
}
83+
84+
pk, err := ssh.NewPublicKeys(user, identity, "")
7185
if err != nil {
7286
return nil, err
7387
}

pkg/git/transport_test.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,25 @@ var (
6666

6767
func TestAuthSecretStrategyForURL(t *testing.T) {
6868
tests := []struct {
69-
name string
70-
url string
71-
want AuthSecretStrategy
69+
name string
70+
url string
71+
want AuthSecretStrategy
72+
wantErr bool
7273
}{
73-
{"HTTP", "http://git.example.com/org/repo.git", &BasicAuth{}},
74-
{"HTTPS", "https://git.example.com/org/repo.git", &BasicAuth{}},
75-
{"SSH", "ssh://git.example.com:2222/org/repo.git", &PublicKeyAuth{}},
76-
{"unsupported", "protocol://example.com", nil},
74+
{"HTTP", "http://git.example.com/org/repo.git", &BasicAuth{}, false},
75+
{"HTTPS", "https://git.example.com/org/repo.git", &BasicAuth{}, false},
76+
{"SSH", "ssh://git.example.com:2222/org/repo.git", &PublicKeyAuth{}, false},
77+
{"SSH with username", "ssh://[email protected]:2222/org/repo.git", &PublicKeyAuth{user: "example"}, false},
78+
{"unsupported", "protocol://example.com", nil, true},
7779
}
7880
for _, tt := range tests {
7981
t.Run(tt.name, func(t *testing.T) {
80-
got := AuthSecretStrategyForURL(tt.url)
81-
if reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
82+
got, err := AuthSecretStrategyForURL(tt.url)
83+
if (err != nil) != tt.wantErr {
84+
t.Errorf("AuthSecretStrategyForURL() error = %v, wantErr %v", err, tt.wantErr)
85+
return
86+
}
87+
if !reflect.DeepEqual(got, tt.want) {
8288
t.Errorf("AuthSecretStrategyForURL() got = %v, want %v", got, tt.want)
8389
}
8490
})

0 commit comments

Comments
 (0)