scrapli / scrapligo

scrapli, but in go!
MIT License
258 stars 42 forks source link

Allow custom regex patterns for channel authentication #193

Closed Jamous closed 2 months ago

Jamous commented 2 months ago

I am working with a network switch, FS 2805s, with a non standard login prompt. See example of the login prompt below:

ssh admin@10.0.0.1
Password(1-32 chars):*************

switch>

When trying to connect to this device using Scrapligo, I get this error: errTimeoutError: channel timeout during in channel ssh authentication

After looking a little bit I believe this is because Scrapli is trying to match the login prompt using this regex pattern (?im)(.*@.*)?password:\s?$ which is in channel/auth.go getAuthPatterns(). This regex pattern does not match the password prompt for this platform. If I manually change this pattern to :$ I can log into this device.

Could we add in an option to pass our own regex patterns to getAuthPatterns (possibly via a custom platform yaml file)?

My code (for reference):

    p, err := platform.NewPlatform(
        //Custom platform
        "fs_s2805",
        host.Address,
        options.WithAuthNoStrictKey(),
        options.WithAuthUsername(host.Username),
        options.WithAuthPassword(host.Password),
        options.WithTransportType("system"),        //Uses /bin/ssh wrapper https://github.com/scrapli/scrapligo/blob/main/driver/options/generic.go#L14
        options.WithSSHConfigFile("~/.ssh/config"), //Specifies ssh config file https://github.com/scrapli/scrapligo/issues/77
    )

    if err != nil {
        errm := fmt.Errorf("Fss2805SetupScrapli failed to create platform on host %s; error: %+v\n\n", host.Address, err)
        return nil, "", errm
    } else {
        fmt.Println("Created platform")
    }

    d, err := p.GetNetworkDriver()
    if err != nil {
        errm := fmt.Errorf("Fss2805SetupScrapli failed to fetch network driver from the platfor mon host %s; error: %+v\n\n", host.Address, err)
        return nil, "", errm
    } else {
        fmt.Println("fetched network driver")
    }

    err = d.Open()
    if err != nil {
        errm := fmt.Errorf("Fss2805SetupScrapli failed to open driver on host %s, error: %+v\n\n", host.Address, err)
        return nil, "", errm
    } else {
        hostm := fmt.Errorf("Fss2805SetupScrapli connected to host %s", host.Address)
        logging.Debug(hostm)
        fmt.Println(hostm)
        fmt.Println("c")
    }

My fs_s2805s platform

---
platform-type: 'fs_s2805'
default:
  driver-type: 'network'
  privilege-levels:
    exec:
      name: 'exec'
      pattern: '>$'
      previous-priv:
      deescalate:
      escalate:
      escalate-auth: false
      escalate-prompt: 
    privilege-exec:
      name: 'privilege-exec'
      pattern: '#$'
      previous-priv: 'exec'
      deescalate: 'disable'
      escalate: 'enable'
      escalate-auth: true
      escalate-prompt: '(?im)^(?:enable\s){0,1}password:\s?$'
    configuration:
      name: 'configuration'
      pattern: '(config)#$'
      not-contains:
        - 'tcl)'
      previous-priv: 'privilege-exec'
      deescalate: 'exit'
      escalate: 'configure terminal'
      escalate-auth: false
      escalate-prompt:
    tclsh:
      name: 'tclsh'
      pattern: '(?im)^([\w.\-@/+>:]+\(tcl\)[>#]|\+>)$'
      previous-priv: 'privilege-exec'
      deescalate: 'tclquit'
      escalate: 'tclsh'
      escalate-auth: false
      escalate-prompt:
  default-desired-privilege-level: 'privilege-exec'
  failed-when-contains:
    - '% Ambiguous command'
    - '% Incomplete command'
    - '% Invalid input detected'
    - '% Unknown command'
  textfsm-platform: 'cisco_iosxe' # ignored in go because no ntc-templates
  network-on-open:
    - operation: 'acquire-priv' # targets default desired priv by default
  network-on-close:
    - operation: 'acquire-priv'
    - operation: 'channel.write'
      input: 'exit'
    - operation: 'channel.return'

My modified getAuthPatterns() function

func getAuthPatterns() *authPatterns {
        authPatternsInstanceOnce.Do(func() {
                authPatternsInstance = &authPatterns{
                        username:   regexp.MustCompile(`(?im)^(.*username:)|(.*login:)\s?$`),
                        password:   regexp.MustCompile(`:$`),
                        //password:   regexp.MustCompile(`(?im)(.*@.*)?password:\s?$`),
                        passphrase: regexp.MustCompile(`(?i)enter passphrase for key`),
                }
        })

        return authPatternsInstance
}
carlmontanari commented 2 months ago

hah, you win the first person to run into this in go :) (much less annoying to tweak in python flavor!).

yea we should add this to the platform definition but I think we already support ya tweaking it with this guy. can ya give that a whack to see if it gets ya rollin?

Jamous commented 2 months ago

hah, you win the first person to run into this in go :) (much less annoying to tweak in python flavor!).

yea we should add this to the platform definition but I think we already support ya tweaking it with this guy. can ya give that a whack to see if it gets ya rollin?

That works perfectly! Thank you, and thanks for your work on this awesome project!

My updated code.

    //Scrapli code
    passPattern := regexp.MustCompile(`:$`) //Regex match for password pattern
    p, err := platform.NewPlatform(
        //Custom platform
        "fs_s2805",
        host.Address,
        options.WithAuthNoStrictKey(),
        options.WithAuthUsername(host.Username),
        options.WithAuthPassword(host.Password),
        options.WithTransportType("system"),        //Uses /bin/ssh wrapper https://github.com/scrapli/scrapligo/blob/main/driver/options/generic.go#L14
        options.WithSSHConfigFile("~/.ssh/config"), //Specifies ssh config file https://github.com/scrapli/scrapligo/issues/77
        options.WithPasswordPattern(passPattern),   //Specifies custom regex for password prompt https://github.com/scrapli/scrapligo/issues/193
    )
carlmontanari commented 2 months ago

boom! thanks for confirming and sharing the final solution! 🤘