diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b8a713d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode +*.exe \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5021a0c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM golang:1.15.0-buster +ADD . /app +WORKDIR /app +ENV GO111MODULE=on +RUN go mod download +CMD go run cis.go \ No newline at end of file diff --git a/cis.go b/cis.go index 108bb5b..bb34b14 100644 --- a/cis.go +++ b/cis.go @@ -1,307 +1,161 @@ package main import ( + "fmt" + "io/ioutil" "log" "strconv" + "strings" "github.com/gliderlabs/ssh" "golang.org/x/crypto/ssh/terminal" ) -// ssh listernet +const ( + defaultHostname = "cisgo1000v" + defaultContextState = ">" + password = "admin" +) + +func readFile(filename string) string { + content, err := ioutil.ReadFile(filename) + if err != nil { + log.Fatal(err) + } + return string(content) +} + +type commandGroup struct { + basic map[string]string + hostname map[string]string + mode map[string]string +} + +func newCommandGroup() *commandGroup { + cmds := new(commandGroup) + cmds.basic = make(map[string]string) + cmds.basic["terminal length 0"] = " " + cmds.basic["terminal width 511"] = " " + cmds.basic["show ip interface brief"] = readFile("config/show_ip_int_bri.txt") + + cmds.hostname = make(map[string]string) + cmds.hostname["show version"] = readFile("config/show_version.txt") + cmds.hostname["show running-config"] = readFile("config/show_running-config.txt") + + cmds.mode = make(map[string]string) + cmds.mode["conf t"] = "(config)#" + cmds.mode["configure terminal"] = "(config)#" + cmds.mode["configure t"] = "(config)#" + cmds.mode["enable"] = "#" + cmds.mode["en"] = "#" + cmds.mode["base"] = ">" + return cmds +} + +type internalState struct { + hostname string + currentMode string // >, #, or (config)# + prompt string +} + +func (s *internalState) setMode(mode string) { + s.currentMode = mode + s.prompt = s.hostname + s.currentMode +} + +func (s *internalState) setHostname(hostname string) { + s.hostname = hostname + s.prompt = s.hostname + s.currentMode +} + +func (s *internalState) exit() bool { + switch s.currentMode { + case ">": + return false + case "#": + s.setMode(">") + case "(config)#": + s.setMode("#") + } + return true +} + +func newState() *internalState { + // log.Println("created new internalState") + return &internalState{defaultHostname, defaultContextState, defaultHostname + defaultContextState} +} + +// ssh listener func sshListener(portNumber int, done chan bool) { - supportedCommands := make(map[string]string) - contextSearch := make(map[string]string) + commandGroup := newCommandGroup() + contextHierarchy := make(map[string]string) - contextSearch["conf t"] = "(config)#" - contextSearch["configure terminal"] = "(config)#" - contextSearch["configure t"] = "(config)#" - contextSearch["enable"] = "#" - contextSearch["en"] = "#" - contextSearch["base"] = ">" - contextHierarchy["(config)#"] = "#" contextHierarchy["#"] = ">" contextHierarchy[">"] = "exit" - hostname := "cisgo1000v" - password := "admin" - - supportedCommands["show version"] = `Cisco IOS XE Software, Version 16.04.01 -Cisco IOS Software [Everest], CSR1000V Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.4.1, RELEASE SOFTWARE (fc2) -Technical Support: http://www.cisco.com/techsupport -Copyright (c) 1986-2016 by Cisco Systems, Inc. -Compiled Sun 27-Nov-16 13:02 by mcpre -Cisco IOS-XE software, Copyright (c) 2005-2016 by cisco Systems, Inc. -All rights reserved. Certain components of Cisco IOS-XE software are -licensed under the GNU General Public License ("GPL") Version 2.0. The -software code licensed under GPL Version 2.0 is free software that comes -with ABSOLUTELY NO WARRANTY. You can redistribute and/or modify such -GPL code under the terms of GPL Version 2.0. For more details, see the -documentation or "License Notice" file accompanying the IOS-XE software, -or the applicable URL provided on the flyer accompanying the IOS-XE -software. -ROM: IOS-XE ROMMON -csr1000v uptime is 4 hours, 55 minutes -Uptime for this control processor is 4 hours, 56 minutes -System returned to ROM by reload -System image file is "bootflash:packages.conf" -Last reload reason: reload -This product contains cryptographic features and is subject to United -States and local country laws governing import, export, transfer and -use. Delivery of Cisco cryptographic products does not imply -third-party authority to import, export, distribute or use encryption. -Importers, exporters, distributors and users are responsible for -compliance with U.S. and local country laws. By using this product you -agree to comply with applicable laws and regulations. If you are unable -to comply with U.S. and local laws, return this product immediately. -A summary of U.S. laws governing Cisco cryptographic products may be found at: -http://www.cisco.com/wwl/export/crypto/tool/stqrg.html -If you require further assistance please contact us by sending email to -export@cisco.com. -License Level: ax -License Type: Default. No valid license found. -Next reload license Level: ax -cisco CSR1000V (VXE) processor (revision VXE) with 2052375K/3075K bytes of memory. -Processor board ID 9FKLJWM5EB0 -10 Gigabit Ethernet interfaces -32768K bytes of non-volatile configuration memory. -3985132K bytes of physical memory. -7774207K bytes of virtual hard disk at bootflash:. -0K bytes of at webui:. -Configuration register is 0x2102` - - supportedCommands["show ip interface brief"] = `Interface IP-Address OK? Method Status Protocol -FastEthernet0/0 10.0.2.27 YES NVRAM up up -Serial0/0 unassigned YES NVRAM administratively down down -FastEthernet0/1 unassigned YES NVRAM administratively down down -Serial0/1 unassigned YES NVRAM administratively down down -FastEthernet1/0 unassigned YES NVRAM administratively down down -FastEthernet2/0 unassigned YES NVRAM administratively down down -FastEthernet3/0 unassigned YES unset up down -FastEthernet3/1 unassigned YES unset up down -FastEthernet3/2 unassigned YES unset up down -FastEthernet3/3 unassigned YES unset up down -FastEthernet3/4 unassigned YES unset up down -FastEthernet3/5 unassigned YES unset up down -FastEthernet3/6 unassigned YES unset up down -FastEthernet3/7 unassigned YES unset up down -FastEthernet3/8 unassigned YES unset up down -FastEthernet3/9 unassigned YES unset up down -FastEthernet3/10 unassigned YES unset up down -FastEthernet3/11 unassigned YES unset up down -FastEthernet3/12 unassigned YES unset up down -FastEthernet3/13 unassigned YES unset up down -FastEthernet3/14 unassigned YES unset up down -FastEthernet3/15 unassigned YES unset up down -Vlan1 unassigned YES NVRAM up down` - - supportedCommands["show running-config"] = `Building configuration... - -Current configuration : 2114 bytes -! -version 12.4 -service timestamps debug datetime msec -service timestamps log datetime msec -no service password-encryption -! -hostname herpa derpa -! -boot-start-marker -boot-end-marker -! -! -no aaa new-model -memory-size iomem 5 -no ip icmp rate-limit unreachable -ip cef -! -! -! -! -no ip domain lookup -ip domain name test -ip auth-proxy max-nodata-conns 3 -ip admission max-nodata-conns 3 -! -! -! -! -! -! -! -! -! -! -! -! -! -! -! -username admin privilege 15 secret 5 $1$M1ce$SKeVGg2lUCPrsLkJMIdWf. -! -! -ip tcp synwait-time 5 -ip ssh version 2 -ip scp server enable -! -! -! -! -! -interface FastEthernet0/0 - description netpalm - ip address 10.0.2.27 255.255.255.0 - duplex auto - speed auto -! -interface Serial0/0 - no ip address - shutdown - clock rate 2000000 -! -interface FastEthernet0/1 - no ip address - shutdown - duplex auto - speed auto -! -interface Serial0/1 - no ip address - shutdown - clock rate 2000000 -! -interface FastEthernet1/0 - no ip address - shutdown - duplex auto - speed auto -! -interface FastEthernet2/0 - no ip address - shutdown - duplex auto - speed auto -! -interface FastEthernet3/0 -! -interface FastEthernet3/1 -! -interface FastEthernet3/2 -! -interface FastEthernet3/3 -! -interface FastEthernet3/4 -! -interface FastEthernet3/5 -! -interface FastEthernet3/6 -! -interface FastEthernet3/7 -! -interface FastEthernet3/8 -! -interface FastEthernet3/9 -! -interface FastEthernet3/10 -! -interface FastEthernet3/11 -! -interface FastEthernet3/12 -! -interface FastEthernet3/13 -! -interface FastEthernet3/14 -! -interface FastEthernet3/15 -! -interface Vlan1 - no ip address -! -ip forward-protocol nd -! -! -no ip http server -no ip http secure-server -! -ip access-list standard bob -ip access-list standard yip -! -snmp-server community test RO -snmp-server community location RO yip -snmp-server community contact RO bob -no cdp log mismatch duplex -! -! -! -control-plane -! -! -! -! -! -! -! -! -! -! -line con 0 - exec-timeout 0 0 - privilege level 15 - logging synchronous -line aux 0 - exec-timeout 0 0 - privilege level 15 - logging synchronous -line vty 0 4 - privilege level 15 - login local - transport input ssh -line vty 5 15 - privilege level 15 - login local - transport input ssh -! -! -end` + thisState := newState() ssh.Handle(func(s ssh.Session) { - // io.WriteString(s, fmt.Sprintf(SHOW_VERSION_PAGING_DISABLED)) - term := terminal.NewTerminal(s, hostname+contextSearch["base"]) - contextState := ">" + term := terminal.NewTerminal(s, thisState.prompt) for { - line, err := term.ReadLine() + response, err := term.ReadLine() if err != nil { break } - response := line - log.Println(line) - if supportedCommands[response] != "" { - // lookup supported commands for response - term.Write(append([]byte(supportedCommands[response]), '\n')) + + log.Println(response) + if response == "reset state" { + log.Println("resetting internal state") + thisState = newState() + term.SetPrompt(thisState.prompt) + } else if response == "" { // return if nothing is entered term.Write(append([]byte(response))) - } else if contextSearch[response] != "" { + + } else if commandGroup.basic[response] != "" { + // lookup supported commands for response + term.Write(append([]byte(commandGroup.basic[response]), '\n')) + + } else if commandGroup.mode[response] != "" { // switch contexts as needed - term.SetPrompt(string(hostname + contextSearch[response])) - contextState = contextSearch[response] - } else if response == "exit" { + thisState.setMode(commandGroup.mode[response]) + term.SetPrompt(thisState.prompt) + + } else if response == "exit" || response == "end" { // drop down configs if required - if contextHierarchy[contextState] == "exit" { + if thisState.exit() { // "true" means we're still active, "false" means we're done + term.SetPrompt(thisState.prompt) + } else { break + } + + } else if commandGroup.hostname[response] != "" { + term.Write([]byte(fmt.Sprintf(commandGroup.hostname[response], thisState.hostname))) + + } else if thisState.currentMode != ">" { // we're in config mode + fields := strings.Fields(response) + command := fields[0] + if command == "hostname" { + thisState.setHostname(strings.Join(fields[1:], " ")) + log.Printf("Setting hostname to %s\n", thisState.hostname) + term.SetPrompt(thisState.prompt) + } else { - term.SetPrompt(string(hostname + contextHierarchy[contextState])) - contextState = contextHierarchy[contextState] + term.Write([]byte("% Ambiguous command: \"" + response + "\"\n")) } + } else { - term.Write(append([]byte("% Ambiguous command: \""+response+"\""), '\n')) + term.Write([]byte("% Ambiguous command: \"" + response + "\"\n")) } + } log.Println("terminal closed") + }) portString := ":" + strconv.Itoa(portNumber) diff --git a/config/show_ip_int_bri.txt b/config/show_ip_int_bri.txt new file mode 100644 index 0000000..7bd3cac --- /dev/null +++ b/config/show_ip_int_bri.txt @@ -0,0 +1,24 @@ +Interface IP-Address OK? Method Status Protocol +FastEthernet0/0 10.0.2.27 YES NVRAM up up +Serial0/0 unassigned YES NVRAM administratively down down +FastEthernet0/1 unassigned YES NVRAM administratively down down +Serial0/1 unassigned YES NVRAM administratively down down +FastEthernet1/0 unassigned YES NVRAM administratively down down +FastEthernet2/0 unassigned YES NVRAM administratively down down +FastEthernet3/0 unassigned YES unset up down +FastEthernet3/1 unassigned YES unset up down +FastEthernet3/2 unassigned YES unset up down +FastEthernet3/3 unassigned YES unset up down +FastEthernet3/4 unassigned YES unset up down +FastEthernet3/5 unassigned YES unset up down +FastEthernet3/6 unassigned YES unset up down +FastEthernet3/7 unassigned YES unset up down +FastEthernet3/8 unassigned YES unset up down +FastEthernet3/9 unassigned YES unset up down +FastEthernet3/10 unassigned YES unset up down +FastEthernet3/11 unassigned YES unset up down +FastEthernet3/12 unassigned YES unset up down +FastEthernet3/13 unassigned YES unset up down +FastEthernet3/14 unassigned YES unset up down +FastEthernet3/15 unassigned YES unset up down +Vlan1 unassigned YES NVRAM up down diff --git a/config/show_running-config.txt b/config/show_running-config.txt new file mode 100644 index 0000000..2b0b1bf --- /dev/null +++ b/config/show_running-config.txt @@ -0,0 +1,168 @@ +Building configuration... + +Current configuration : 2114 bytes +! +version 12.4 +service timestamps debug datetime msec +service timestamps log datetime msec +no service password-encryption +! +hostname %[1]s +! +boot-start-marker +boot-end-marker +! +! +no aaa new-model +memory-size iomem 5 +no ip icmp rate-limit unreachable +ip cef +! +! +! +! +no ip domain lookup +ip domain name test +ip auth-proxy max-nodata-conns 3 +ip admission max-nodata-conns 3 +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +username admin privilege 15 secret 5 $1$M1ce$SKeVGg2lUCPrsLkJMIdWf. +! +! +ip tcp synwait-time 5 +ip ssh version 2 +ip scp server enable +! +! +! +! +! +interface FastEthernet0/0 +description netpalm +ip address 10.0.2.27 255.255.255.0 +duplex auto +speed auto +! +interface Serial0/0 +no ip address +shutdown +clock rate 2000000 +! +interface FastEthernet0/1 +no ip address +shutdown +duplex auto +speed auto +! +interface Serial0/1 +no ip address +shutdown +clock rate 2000000 +! +interface FastEthernet1/0 +no ip address +shutdown +duplex auto +speed auto +! +interface FastEthernet2/0 +no ip address +shutdown +duplex auto +speed auto +! +interface FastEthernet3/0 +! +interface FastEthernet3/1 +! +interface FastEthernet3/2 +! +interface FastEthernet3/3 +! +interface FastEthernet3/4 +! +interface FastEthernet3/5 +! +interface FastEthernet3/6 +! +interface FastEthernet3/7 +! +interface FastEthernet3/8 +! +interface FastEthernet3/9 +! +interface FastEthernet3/10 +! +interface FastEthernet3/11 +! +interface FastEthernet3/12 +! +interface FastEthernet3/13 +! +interface FastEthernet3/14 +! +interface FastEthernet3/15 +! +interface Vlan1 +no ip address +! +ip forward-protocol nd +! +! +no ip http server +no ip http secure-server +! +ip access-list standard bob +ip access-list standard yip +! +snmp-server community test RO +snmp-server community location RO yip +snmp-server community contact RO bob +no cdp log mismatch duplex +! +! +! +control-plane +! +! +! +! +! +! +! +! +! +! +line con 0 +exec-timeout 0 0 +privilege level 15 +logging synchronous +line aux 0 +exec-timeout 0 0 +privilege level 15 +logging synchronous +line vty 0 4 +privilege level 15 +login local +transport input ssh +line vty 5 15 +privilege level 15 +login local +transport input ssh +! +! +end diff --git a/config/show_version.txt b/config/show_version.txt new file mode 100644 index 0000000..7b7c703 --- /dev/null +++ b/config/show_version.txt @@ -0,0 +1,43 @@ +Cisco IOS XE Software, Version 16.04.01 +Cisco IOS Software [Everest], CSR1000V Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.4.1, RELEASE SOFTWARE (fc2) +Technical Support: http://www.cisco.com/techsupport +Copyright (c) 1986-2016 by Cisco Systems, Inc. +Compiled Sun 27-Nov-16 13:02 by mcpre +Cisco IOS-XE software, Copyright (c) 2005-2016 by cisco Systems, Inc. +All rights reserved. Certain components of Cisco IOS-XE software are +licensed under the GNU General Public License ("GPL") Version 2.0. The +software code licensed under GPL Version 2.0 is free software that comes +with ABSOLUTELY NO WARRANTY. You can redistribute and/or modify such +GPL code under the terms of GPL Version 2.0. For more details, see the +documentation or "License Notice" file accompanying the IOS-XE software, +or the applicable URL provided on the flyer accompanying the IOS-XE +software. +ROM: IOS-XE ROMMON +%[1]v uptime is 4 hours, 55 minutes +Uptime for this control processor is 4 hours, 56 minutes +System returned to ROM by reload +System image file is "bootflash:packages.conf" +Last reload reason: reload +This product contains cryptographic features and is subject to United +States and local country laws governing import, export, transfer and +use. Delivery of Cisco cryptographic products does not imply +third-party authority to import, export, distribute or use encryption. +Importers, exporters, distributors and users are responsible for +compliance with U.S. and local country laws. By using this product you +agree to comply with applicable laws and regulations. If you are unable +to comply with U.S. and local laws, return this product immediately. +A summary of U.S. laws governing Cisco cryptographic products may be found at: +http://www.cisco.com/wwl/export/crypto/tool/stqrg.html +If you require further assistance please contact us by sending email to +export@cisco.com. +License Level: ax +License Type: Default. No valid license found. +Next reload license Level: ax +cisco CSR1000V (VXE) processor (revision VXE) with 2052375K/3075K bytes of memory. +Processor board ID 9FKLJWM5EB0 +10 Gigabit Ethernet interfaces +32768K bytes of non-volatile configuration memory. +3985132K bytes of physical memory. +7774207K bytes of virtual hard disk at bootflash:. +0K bytes of at webui:. +Configuration register is 0x2102 diff --git a/vendor/golang.org/x/crypto/ssh/terminal/terminal.go b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go index 0dd1651..aa2013a 100644 --- a/vendor/golang.org/x/crypto/ssh/terminal/terminal.go +++ b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go @@ -523,7 +523,7 @@ func (t *Terminal) handleKey(key rune) (line string, ok bool) { t.setLine(runes, len(runes)) } } - case keyNewline: //replaced keyEnter + case keyEnter, keyNewline: //replaced keyEnter t.moveCursorToPos(len(t.line)) t.queue([]rune("\r\n")) line = string(t.line)