gossh

Golang crypto ssh package: attempted methods [none]


I am trying to ssh into Arista EOS Switch by using golang package: golang.org/x/crypto/ssh, the idea is to ssh into switch, execute some commands and get output.

here is my code:

func ExecuteCommands(commands []string) string {
    config := &ssh.ClientConfig{
        User: n.Username,
        Auth: []ssh.AuthMethod{
            ssh.Password(n.Password),
        },
        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
    }

    client, err := ssh.Dial("tcp", n.IpAddress + ":" + n.Port, config)
    if err != nil {
        msg := fmt.Sprintf("Failed to connect to host: %v on port 22, error: %v, Username: %v, Password: %v", n.IpAddress, err, n.Username, n.Password)
        return msg
    }
    defer client.Close()

    session, err := client.NewSession()
    if err != nil {
        msg := fmt.Sprintf("Failed to create a session with client: %v", err.Error())
        return msg
    }
    defer session.Close()
    stdin, err := session.StdinPipe()
    if err != nil {
        log.Fatalf("Unable to setup stdin for session: %v", err)
    }
    stdout, err := session.StdoutPipe()
    if err != nil {
        log.Fatalf("Unable to setup stdout for session: %v", err)
    }
    stderr, err := session.StderrPipe()
    if err != nil {
        log.Fatalf("Unable to setup stderr for session: %v", err)
    }

    output := ""

    // Start the remote shell
    if err := session.Shell(); err != nil {
        log.Fatalf("Failed to start shell: %v", err)
    }

    // Goroutine to read stdout
    go func() {
        scanner := bufio.NewScanner(stdout)
        for scanner.Scan() {
            output += scanner.Text()
        }
    }()

    // Goroutine to read stderr
    go func() {
        scanner := bufio.NewScanner(stderr)
        for scanner.Scan() {
            output += scanner.Text()
        }
    }()

    // Send commands
    writer := bufio.NewWriter(stdin)

    for _, cmd := range commands {
        _, err := writer.WriteString(cmd + "\n")
        if err != nil {
            log.Printf("Error writing command: %v", err)
            break
        }
        writer.Flush()
        time.Sleep(500 * time.Millisecond) // Give time for output to appear
    }

    // Close stdin to signal end of input
    stdin.Close()

    session.Wait()  
    return output
}

While this code works perfectly fine for linux device it fails with following error for Arista EOS Switch:

error: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none], no supported methods remain,

I am suspecting that there are some setting in golang crypto package with which we can solve this issue but I am unable to find such a configuration, please help.


Solution

  • As suggested by @kostix, I did ssh into server by using -v and found that it was asking for keyboard-interactive for Auth mode, so here is the updated method:

    func ExecuteCommands(commands []string) string {
        Ciphers := ssh.InsecureAlgorithms().Ciphers
        Ciphers = append(Ciphers, ssh.SupportedAlgorithms().Ciphers...)
        KeyExchanges := ssh.InsecureAlgorithms().KeyExchanges
        KeyExchanges = append(KeyExchanges, ssh.SupportedAlgorithms().KeyExchanges...)
        Macs := ssh.InsecureAlgorithms().MACs
        Macs = append(Macs, ssh.SupportedAlgorithms().MACs...)
        config := &ssh.ClientConfig{
            User: n.Username,
            Auth: []ssh.AuthMethod{
                ssh.Password(n.Password),
                ssh.KeyboardInteractive(func(user, instruction string, questions []string, echon []bool) ([]string, error) {
                    // The server is prompting for a password
                    if len(questions) == 1 && strings.Contains(strings.TrimSpace(strings.ToLower(questions[0])), "password:") {
                        return []string{n.Password}, nil
                    }
                    return nil, nil
                }),
            },
            HostKeyCallback: ssh.InsecureIgnoreHostKey(),
            Config: ssh.Config{
                Ciphers: Ciphers,
                KeyExchanges: KeyExchanges,
                MACs: Macs,
            },
        }
    
        client, err := ssh.Dial("tcp", n.IpAddress + ":" + n.Port, config)
        if err != nil {
            msg := fmt.Sprintf("Failed to connect to host: %v on port 22, error: %v, Username: %v, Password: %v", n.IpAddress, err, n.Username, n.Password)
            return msg
        }
        defer client.Close()
    
        session, err := client.NewSession()
        if err != nil {
            msg := fmt.Sprintf("Failed to create a session with client: %v", err.Error())
            return msg
        }
        defer session.Close()
        stdin, err := session.StdinPipe()
        if err != nil {
            log.Fatalf("Unable to setup stdin for session: %v", err)
        }
        stdout, err := session.StdoutPipe()
        if err != nil {
            log.Fatalf("Unable to setup stdout for session: %v", err)
        }
        stderr, err := session.StderrPipe()
        if err != nil {
            log.Fatalf("Unable to setup stderr for session: %v", err)
        }
    
        output := ""
    
        // Start the remote shell
        if err := session.Shell(); err != nil {
            log.Fatalf("Failed to start shell: %v", err)
        }
    
        // Goroutine to read stdout
        go func() {
            scanner := bufio.NewScanner(stdout)
            for scanner.Scan() {
                output += scanner.Text()
            }
        }()
    
        // Goroutine to read stderr
        go func() {
            scanner := bufio.NewScanner(stderr)
            for scanner.Scan() {
                output += scanner.Text()
            }
        }()
    
        // Send commands
        writer := bufio.NewWriter(stdin)
    
        for _, cmd := range commands {
            _, err := writer.WriteString(cmd + "\n")
            if err != nil {
                log.Printf("Error writing command: %v", err)
                break
            }
            writer.Flush()
            time.Sleep(500 * time.Millisecond) // Give time for output to appear
        }
    
        // Close stdin to signal end of input
        stdin.Close()
    
        // Wait for the session to finish (optional, depending on your needs)
        session.Wait()  
        return output
    }