Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add flag to allow configuration of SSH kex algos #655

Merged
merged 1 commit into from
Apr 7, 2022
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
9 changes: 9 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
"github.com/fluxcd/source-controller/controllers"
"github.com/fluxcd/source-controller/internal/cache"
"github.com/fluxcd/source-controller/internal/helm"
"github.com/fluxcd/source-controller/pkg/git"
"github.com/fluxcd/source-controller/pkg/git/libgit2/managed"
// +kubebuilder:scaffold:imports
)
Expand Down Expand Up @@ -90,6 +91,7 @@ func main() {
helmCacheMaxSize int
helmCacheTTL string
helmCachePurgeInterval string
kexAlgos []string
)

flag.StringVar(&metricsAddr, "metrics-addr", envOrDefault("METRICS_ADDR", ":8080"),
Expand Down Expand Up @@ -120,6 +122,8 @@ func main() {
"The TTL of an index in the cache. Valid time units are ns, us (or µs), ms, s, m, h.")
flag.StringVar(&helmCachePurgeInterval, "helm-cache-purge-interval", "1m",
"The interval at which the cache is purged. Valid time units are ns, us (or µs), ms, s, m, h.")
flag.StringSliceVar(&kexAlgos, "ssh-kex-algos", []string{},
"The list of key exchange algorithms to use for ssh connections, arranged from most preferred to the least.")

clientOptions.BindFlags(flag.CommandLine)
logOptions.BindFlags(flag.CommandLine)
Expand Down Expand Up @@ -174,6 +178,7 @@ func main() {
storageAdvAddr = determineAdvStorageAddr(storageAddr, setupLog)
}
storage := mustInitStorage(storagePath, storageAdvAddr, setupLog)
setPreferredKexAlgos(kexAlgos)

if err = (&controllers.GitRepositoryReconciler{
Client: mgr.GetClient(),
Expand Down Expand Up @@ -333,3 +338,7 @@ func envOrDefault(envName, defaultValue string) string {

return defaultValue
}

func setPreferredKexAlgos(algos []string) {
git.KexAlgos = algos
}
32 changes: 31 additions & 1 deletion pkg/git/gogit/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"github.com/fluxcd/pkg/ssh/knownhosts"

"github.com/fluxcd/source-controller/pkg/git"

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

// transportAuth constructs the transport.AuthMethod for the git.Transport of
Expand Down Expand Up @@ -58,7 +60,10 @@ func transportAuth(opts *git.AuthOptions) (transport.AuthMethod, error) {
}
pk.HostKeyCallback = callback
}
return pk, nil
customPK := &CustomPublicKeys{
pk: pk,
}
return customPK, nil
}
case "":
return nil, fmt.Errorf("no transport type set")
Expand All @@ -75,3 +80,28 @@ func caBundle(opts *git.AuthOptions) []byte {
}
return opts.CAFile
}

// CustomPublicKeys is a wrapper around ssh.PublicKeys to help us
// customize the ssh config. It implements ssh.AuthMethod.
type CustomPublicKeys struct {
pk *ssh.PublicKeys
}

func (a *CustomPublicKeys) Name() string {
return a.pk.Name()
}

func (a *CustomPublicKeys) String() string {
return a.pk.String()
}

func (a *CustomPublicKeys) ClientConfig() (*gossh.ClientConfig, error) {
config, err := a.pk.ClientConfig()
if err != nil {
return nil, err
}
if len(git.KexAlgos) > 0 {
config.Config.KeyExchanges = git.KexAlgos
}
return config, nil
}
47 changes: 36 additions & 11 deletions pkg/git/gogit/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (

"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/onsi/gomega"

"github.com/fluxcd/source-controller/pkg/git"
Expand Down Expand Up @@ -72,6 +71,7 @@ func Test_transportAuth(t *testing.T) {
name string
opts *git.AuthOptions
wantFunc func(g *WithT, t transport.AuthMethod, opts *git.AuthOptions)
kexAlgos []string
wantErr error
}{
{
Expand Down Expand Up @@ -128,10 +128,10 @@ func Test_transportAuth(t *testing.T) {
Identity: []byte(privateKeyFixture),
},
wantFunc: func(g *WithT, t transport.AuthMethod, opts *git.AuthOptions) {
tt, ok := t.(*ssh.PublicKeys)
tt, ok := t.(*CustomPublicKeys)
g.Expect(ok).To(BeTrue())
g.Expect(tt.User).To(Equal(opts.Username))
g.Expect(tt.Signer.PublicKey().Type()).To(Equal("ssh-rsa"))
g.Expect(tt.pk.User).To(Equal(opts.Username))
g.Expect(tt.pk.Signer.PublicKey().Type()).To(Equal("ssh-rsa"))
},
},
{
Expand All @@ -143,10 +143,31 @@ func Test_transportAuth(t *testing.T) {
Identity: []byte(privateKeyPassphraseFixture),
},
wantFunc: func(g *WithT, t transport.AuthMethod, opts *git.AuthOptions) {
tt, ok := t.(*ssh.PublicKeys)
tt, ok := t.(*CustomPublicKeys)
g.Expect(ok).To(BeTrue())
g.Expect(tt.User).To(Equal(opts.Username))
g.Expect(tt.Signer.PublicKey().Type()).To(Equal("ssh-rsa"))
g.Expect(tt.pk.User).To(Equal(opts.Username))
g.Expect(tt.pk.Signer.PublicKey().Type()).To(Equal("ssh-rsa"))
},
},
{
name: "SSH with custom key exchanges",
opts: &git.AuthOptions{
Transport: git.SSH,
Username: "example",
Identity: []byte(privateKeyFixture),
KnownHosts: []byte(knownHostsFixture),
},
kexAlgos: []string{"curve25519-sha256", "diffie-hellman-group-exchange-sha256"},
wantFunc: func(g *WithT, t transport.AuthMethod, opts *git.AuthOptions) {
tt, ok := t.(*CustomPublicKeys)
g.Expect(ok).To(BeTrue())
g.Expect(tt.pk.User).To(Equal(opts.Username))
g.Expect(tt.pk.Signer.PublicKey().Type()).To(Equal("ssh-rsa"))
config, err := tt.ClientConfig()
g.Expect(err).ToNot(HaveOccurred())
g.Expect(config.Config.KeyExchanges).To(Equal(
[]string{"curve25519-sha256", "diffie-hellman-group-exchange-sha256"}),
)
},
},
{
Expand All @@ -168,11 +189,11 @@ func Test_transportAuth(t *testing.T) {
KnownHosts: []byte(knownHostsFixture),
},
wantFunc: func(g *WithT, t transport.AuthMethod, opts *git.AuthOptions) {
tt, ok := t.(*ssh.PublicKeys)
tt, ok := t.(*CustomPublicKeys)
g.Expect(ok).To(BeTrue())
g.Expect(tt.User).To(Equal(opts.Username))
g.Expect(tt.Signer.PublicKey().Type()).To(Equal("ssh-rsa"))
g.Expect(tt.HostKeyCallback).ToNot(BeNil())
g.Expect(tt.pk.User).To(Equal(opts.Username))
g.Expect(tt.pk.Signer.PublicKey().Type()).To(Equal("ssh-rsa"))
g.Expect(tt.pk.HostKeyCallback).ToNot(BeNil())
},
},
{
Expand Down Expand Up @@ -202,6 +223,10 @@ func Test_transportAuth(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)

if len(tt.kexAlgos) > 0 {
git.KexAlgos = tt.kexAlgos
}

got, err := transportAuth(tt.opts)
if tt.wantErr != nil {
g.Expect(err).To(Equal(tt.wantErr))
Expand Down
4 changes: 4 additions & 0 deletions pkg/git/libgit2/managed/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import (

"golang.org/x/crypto/ssh"

"github.com/fluxcd/source-controller/pkg/git"
git2go "github.com/libgit2/git2go/v33"
)

Expand Down Expand Up @@ -344,6 +345,9 @@ func cacheKeyAndConfig(remoteAddress string, cred *git2go.Credential) (string, *
Auth: []ssh.AuthMethod{ssh.PublicKeys(key)},
Timeout: sshConnectionTimeOut,
}
if len(git.KexAlgos) > 0 {
cfg.Config.KeyExchanges = git.KexAlgos
}

return ck, cfg, nil
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/git/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ type AuthOptions struct {
CAFile []byte
}

// List of custom key exchange algorithms to be used for ssh connections.
var KexAlgos []string

// Validate the AuthOptions against the defined Transport.
func (o AuthOptions) Validate() error {
switch o.Transport {
Expand Down