mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-30 15:07:49 -04:00
plugins: work around x/crypto rejecting ssh-mldsa44-ed25519 known_hosts entries
fixes #4029 port 2.0
This commit is contained in:
1 parent
a0b74b2acf
commit
7ad1b7ef1d
4 files changed
+143
-10
No files matched your search
+2
-2
@@ -15,6 +15,7 @@ require (
|
||||
github.com/go-git/go-git/v6 v6.0.0-beta.1
|
||||
github.com/godbus/dbus/v5 v5.2.4
|
||||
github.com/holoplot/go-evdev v0.0.0-20260909133534-d873ed210bda
|
||||
github.com/kevinburke/ssh_config v1.6.0
|
||||
github.com/klauspost/compress v1.20.3
|
||||
github.com/lucasb-eyer/go-colorful v1.4.1
|
||||
github.com/mattn/go-isatty v0.0.24
|
||||
@@ -31,6 +32,7 @@ require (
|
||||
github.com/yuin/goldmark v1.8.6
|
||||
go.etcd.io/bbolt v1.5.0
|
||||
go4.org/mem v0.0.0-20240501181205-ae6ca9944745
|
||||
golang.org/x/crypto v0.58.0
|
||||
golang.org/x/image v0.47.0
|
||||
golang.org/x/sys v0.49.0
|
||||
golang.org/x/text v0.43.0
|
||||
@@ -71,7 +73,6 @@ require (
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jsimonetti/rtnetlink v1.4.2 // indirect
|
||||
github.com/kevinburke/ssh_config v1.6.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20260802145828-341c2f0c90b5 // indirect
|
||||
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.31 // indirect
|
||||
@@ -97,7 +98,6 @@ require (
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
go4.org/netipx v0.0.0-20260823151212-3075585bcbeb // indirect
|
||||
golang.org/x/crypto v0.58.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20261014143641-f0251eb4aa3e // indirect
|
||||
golang.org/x/net v0.61.0 // indirect
|
||||
golang.org/x/sync v0.24.0 // indirect
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package gitssh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-git/go-git/v6/plumbing/client"
|
||||
"github.com/go-git/go-git/v6/plumbing/transport"
|
||||
xssh "github.com/go-git/go-git/v6/plumbing/transport/ssh"
|
||||
"github.com/go-git/go-git/v6/plumbing/transport/ssh/knownhosts"
|
||||
"github.com/kevinburke/ssh_config"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
func ClientOptions() []client.Option {
|
||||
return []client.Option{client.WithSSHAuth(agentAuth{})}
|
||||
}
|
||||
|
||||
type agentAuth struct{}
|
||||
|
||||
func (agentAuth) ClientConfig(ctx context.Context, req *transport.Request) (*gossh.ClientConfig, error) {
|
||||
auth, err := xssh.NewSSHAgentAuth(username(req.URL))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db, err := knownHostsDB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth.HostKeyCallback = db.HostKeyCallback()
|
||||
cfg, err := auth.ClientConfig(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.HostKeyAlgorithms = db.HostKeyAlgorithms(hostWithPort(req.URL))
|
||||
if len(cfg.HostKeyAlgorithms) == 0 {
|
||||
cfg.HostKeyAlgorithms = gossh.SupportedAlgorithms().HostKeys
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func username(u *url.URL) string {
|
||||
if u.User == nil || u.User.Username() == "" {
|
||||
return xssh.DefaultUsername
|
||||
}
|
||||
return u.User.Username()
|
||||
}
|
||||
|
||||
func hostWithPort(u *url.URL) string {
|
||||
hostname := u.Hostname()
|
||||
port := u.Port()
|
||||
if configHost := ssh_config.Get(hostname, "Hostname"); configHost != "" {
|
||||
hostname = configHost
|
||||
}
|
||||
if port == "" {
|
||||
if configPort := ssh_config.Get(u.Hostname(), "Port"); configPort != "" {
|
||||
if _, err := strconv.Atoi(configPort); err == nil {
|
||||
port = configPort
|
||||
}
|
||||
}
|
||||
}
|
||||
if port == "" {
|
||||
port = strconv.Itoa(xssh.DefaultPort)
|
||||
}
|
||||
return net.JoinHostPort(hostname, port)
|
||||
}
|
||||
|
||||
// x/crypto rejects a whole known_hosts file over one key type it cannot parse,
|
||||
// so entries like ssh-mldsa44-ed25519 are blanked before the file is loaded.
|
||||
func knownHostsDB() (*knownhosts.HostKeyDB, error) {
|
||||
dir, err := os.MkdirTemp("", "dms-known-hosts-")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
var filtered []string
|
||||
for i, file := range knownHostsFiles() {
|
||||
content, err := os.ReadFile(file)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path := filepath.Join(dir, fmt.Sprintf("%d-%s", i, filepath.Base(file)))
|
||||
if err := os.WriteFile(path, blankUnparseableLines(content), 0o600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filtered = append(filtered, path)
|
||||
}
|
||||
if len(filtered) == 0 {
|
||||
return nil, errors.New("unable to find any valid known_hosts file, set SSH_KNOWN_HOSTS env variable")
|
||||
}
|
||||
return knownhosts.NewDB(filtered...)
|
||||
}
|
||||
|
||||
func knownHostsFiles() []string {
|
||||
if files := filepath.SplitList(os.Getenv("SSH_KNOWN_HOSTS")); len(files) != 0 {
|
||||
return files
|
||||
}
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return []string{"/etc/ssh/ssh_known_hosts"}
|
||||
}
|
||||
return []string{filepath.Join(home, ".ssh", "known_hosts"), "/etc/ssh/ssh_known_hosts"}
|
||||
}
|
||||
|
||||
func blankUnparseableLines(content []byte) []byte {
|
||||
lines := bytes.Split(content, []byte("\n"))
|
||||
for i, line := range lines {
|
||||
trimmed := bytes.TrimSpace(line)
|
||||
if len(trimmed) == 0 || trimmed[0] == '#' {
|
||||
continue
|
||||
}
|
||||
if _, _, _, _, _, err := gossh.ParseKnownHosts(trimmed); err != nil {
|
||||
lines[i] = nil
|
||||
}
|
||||
}
|
||||
return bytes.Join(lines, []byte("\n"))
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/duxlabs-dev/dux-shell/core/internal/gitssh"
|
||||
"github.com/duxlabs-dev/dux-shell/core/internal/registries"
|
||||
"github.com/go-git/go-git/v6"
|
||||
"github.com/go-git/go-git/v6/plumbing"
|
||||
@@ -44,8 +45,9 @@ type realGitClient struct{}
|
||||
|
||||
func (g *realGitClient) PlainClone(path string, url string) error {
|
||||
_, err := git.PlainClone(path, &git.CloneOptions{
|
||||
URL: url,
|
||||
Progress: os.Stdout,
|
||||
URL: url,
|
||||
Progress: os.Stdout,
|
||||
ClientOptions: gitssh.ClientOptions(),
|
||||
})
|
||||
return err
|
||||
}
|
||||
@@ -61,7 +63,7 @@ func (g *realGitClient) Pull(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = worktree.Pull(&git.PullOptions{})
|
||||
err = worktree.Pull(&git.PullOptions{ClientOptions: gitssh.ClientOptions()})
|
||||
if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
|
||||
return err
|
||||
}
|
||||
@@ -101,7 +103,7 @@ func (g *realGitClient) HasUpdates(path string) (bool, string, string, error) {
|
||||
return false, "", "", err
|
||||
}
|
||||
|
||||
refs, err := remote.List(&git.ListOptions{})
|
||||
refs, err := remote.List(&git.ListOptions{ClientOptions: gitssh.ClientOptions()})
|
||||
if err != nil {
|
||||
return false, "", "", err
|
||||
}
|
||||
@@ -168,7 +170,7 @@ func (g *realGitClient) CheckoutRevision(path string, revision string) error {
|
||||
if _, remoteErr := repo.Remote("origin"); remoteErr != nil {
|
||||
return fmt.Errorf("revision %s is not available in repository: %w", revision, objectErr)
|
||||
}
|
||||
fetchErr := repo.Fetch(&git.FetchOptions{})
|
||||
fetchErr := repo.Fetch(&git.FetchOptions{ClientOptions: gitssh.ClientOptions()})
|
||||
if fetchErr != nil && fetchErr != git.NoErrAlreadyUpToDate {
|
||||
return fmt.Errorf("failed to fetch locked revision: %w", fetchErr)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/duxlabs-dev/dux-shell/core/internal/gitssh"
|
||||
"github.com/duxlabs-dev/dux-shell/core/internal/registries"
|
||||
"github.com/go-git/go-git/v6"
|
||||
"github.com/spf13/afero"
|
||||
@@ -157,8 +158,9 @@ type realGitClient struct{}
|
||||
|
||||
func (g *realGitClient) PlainClone(path string, url string) error {
|
||||
_, err := git.PlainClone(path, &git.CloneOptions{
|
||||
URL: url,
|
||||
Progress: os.Stdout,
|
||||
URL: url,
|
||||
Progress: os.Stdout,
|
||||
ClientOptions: gitssh.ClientOptions(),
|
||||
})
|
||||
return err
|
||||
}
|
||||
@@ -174,7 +176,7 @@ func (g *realGitClient) Pull(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = worktree.Pull(&git.PullOptions{})
|
||||
err = worktree.Pull(&git.PullOptions{ClientOptions: gitssh.ClientOptions()})
|
||||
if err != nil && err.Error() != "already up-to-date" {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user