Managing network devices with Golang using Netrasp

  • by Patrick Ogenstad
  • April 12, 2021

Have you let the Gophers into your network yet? With Netrasp, you can let them roam wild. Netrasp is a Go package that connects to network devices with SSH to allow you to send commands and configuration to them. The rasping sound as your network gets screenscraped would come from Netrasp. For people coming from the Python world, you could compare Netrasp to Netmiko. Hello Netrasp

Getting started with Netrasp

A simple program using Netrasp could look like this.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/networklore/netrasp/pkg/netrasp"
)


func main() {
	device, err := netrasp.New("router1",
		netrasp.WithUsernamePassword("my_user", "my_password"),
		netrasp.WithDriver("ios"),
	)

	if err != nil {
		log.Fatalf("unable to initialize device: %v", err)
	}

	err = device.Dial(context.Background())
	if err != nil {
		log.Fatalf("unable to connect: %v", err)
	}
	defer device.Close(context.Background())

	output, err := device.Run(context.Background(), "show version")
	if err != nil {
		log.Fatalf("unable to run command: %v", err)
	}
	fmt.Println(output)
}

Configuring devices with Netrasp

If you instead wanted to send configuration commands to a device, a code snippet could look like this.

config := []string{
    "ip access-list extended SOME-TRAFFIC",
    " permit tcp any any eq 22",
    " permit tcp any any eq 443",
}
_, err = device.Configure(context.Background(), config)
if err != nil {
    log.Fatalf("unable to configure device: %v", err)
}

_, err = device.Run(context.Background(), "write memory")
if err != nil {
    log.Fatalf("unable to save config: %v", err)
}

Connection options

If the default connection options don’t work, you might need to change them. An example is when I connect to an older device with the above program, I get this output:

2021/03/09 07:54:51 unable to connect: unable to open connection: unable to establish connection: ssh: handshake failed: ssh: no common algorithm for client to server cipher; client offered: [aes128-gcm@openssh.com chacha20-poly1305@openssh.com aes128-ctr aes192-ctr aes256-ctr], server offered: [aes128-cbc 3des-cbc aes192-cbc aes256-cbc] exit status 1

The problem here is that the network device doesn’t support modern crypto algorithms, and we need to tell Netrasp to downgrade the security using the WithSSHCipher option.

device, err := netrasp.New("router1",
    netrasp.WithUsernamePassword("my_user", "my_password"),
    netrasp.WithDriver("ios")
        netrasp.WithSSHCipher("aes128-cbc"),
)

Another such option is the WithInsecureIgnoreHostKey(), which would disable validation of the public SSH key of the network device against a known_hosts file. By default, Netrasp validates keys that exist in /etc/ssh/ssh_known_hosts or the user’s home directory (~/.ssh/known_hosts). For now, there’s no option to add any new unknown keys with Netrasp.

Netrasp device support

The initial release of Netrasp comes with support for Cisco IOS, Cisco NXOS, and Cisco ASA. As you’ve seen from above, you specify the platform using the WithDriver option, currently choosing one of “asa”, “ios”, or “nxos.”

It should be reasonably simple to add support for additional drivers in the future.

Room for improvement

With the initial release, Netrasp mostly cares about if the underlying SSH transport is working and can find the network devices’ prompts after running a command. It doesn’t care about the syntax of commands or configuration and leaves that up to the user. An example of what this might look like can be seen with this code.

config := []string{
    "ip access-list extended SOME-TRAFFIC",
    " permit tcp any any eq 22",
    " permit tcp any any 80",
    " permit tcp any any eq 443",
}
output, err := device.Configure(context.Background(), config)
if err != nil {
    log.Fatalf("unable to configure device: %v", err)
}
fmt.Println(output)

Here I’ve made a syntax error and missed the “eq” in front of port 80, so the configuration is invalid. It’s still possible to see these kinds of mistakes. Printing the output will show us this:

 permit tcp any any 80
                    ^
% Invalid input detected at '^' marker.

The current version of Netrasp doesn’t treat this as an error and just continues entering the rest of the commands. I have some thoughts that this behavior could be configurable in the future where the config is reported. Perhaps the Configure method would also return a slice with all of the sent commands along with any output. That way, it would be easier to know what config should be reverted if needed.

Another issue is that Netrasp doesn’t keep track of the current prompt of the devices. This could be problematic if you were to run the Enable() method while you already reside in the device’s privileged mode.

Netrasp and Gornir

While you can, of course, run Netrasp as any other Go package as done above, another way of using it would be to integrate it with Gornir. That way, you would get a similar experience as when using Netmiko and Nornir in Python.

Future development of Netrasp

Any future development of Netrasp will, to a large extent, be driven by interest from the community. If there’s no or little interest, I probably won’t spend much more time on it. So, take it for a test drive and see what you think. A word of warning, as indicated in the project’s readme file, this is an early version, and there’s a good chance that some parts of the API change before it’s settled and an initial real version is released. After that Netrasp will follow semver.

Project details

Netrasp is available at Github: https://github.com/networklore/netrasp

Please let me know what you think!