Netmiko
Multi-vendor library to simplify SSH connections to network devices. Utilizes the Python library Netmiko for connections. Supports SSH Key authentication and username / password.
Utilities · Netmiko
Details
| ID | Netmiko |
|---|---|
| Provider | Open Source |
| Category | Utilities |
| From Version | 6.0.0 |
| Docker Image | demisto/netmiko:1.0.0.9067966 |
| Supported Modules | Agentix XSIAM |
README
Netmiko SSH module integration
This integration provides ssh-based access to network devices, servers, and other appliances that support this method of configuration. For a complete list of supported platforms, please visit the below URL:
Netmiko Platforms.md on Github
Configure Netmiko Integration in Cortex XSOAR
- Navigate to Settings - Integrations
- Search for Netmiko
- Click Add instance to create and configure a new integration instance.
- Name: a name for the integration instance.
- Platform: the platform identifier taken from the above SSH or Telnet platform name lists (e.g., linux_ssh, paloalto_panos, etc.)
NOTE: Platform names are taken from the supported
SSH or Telnet device type lists on GitHub.- Hostname: The IP address, hostname, or FQDN for the device to connect to via SSH.
- Port: The port to connect to via SSH
- Credentials: The username/password, or XSOAR credential object, to be used for the connection
- Override the default timeout value: Override the timeout value (in seconds) for a given integration instance. This is useful for devices that are slow in responding with requested output over SSH.
- Click Test to validate the new instance. This performs a simple connection to the system hosting the SSH server.
Commands
The Netmiko integration currently only supports the netmiko-cmds command for SSH. This command can be used via the XSOAR CLI, as part of an automation, or as a task in an XSOAR playbook. Like other XSOAR commands, this object can be passed a single command, a list of commands, or an array of commands to execute in a single session.
netmiko-cmds
Executes a command, or series of commands, over an SSH connection. Outputs from the executed commands are returned to the incident/playground context.
Base Command
netmiko-cmds
Input
| Argument Name | Description | Required |
|---|---|---|
| cmds | The command, or commands, to execute. When commands are manually specified and executed via the XSOAR CLI or in a task, place each command after the first on a new line (no comma required) | Required |
| disable_context | The package ID. Package ID or package name is required. When both exist, ID is used. | Optional |
| exit_argument | The optional exit command to be executed after the cmds parameter. This is tied to the requires_exit optional parameter. (Default: q) | Optional |
| isConfig | Specifies whether or not the commands being executed require a configure command to be executed first (e.g., conf t for Cisco IOS). The specific configure command is handled by the Netmiko Python module, and is associated with the Platform parameter specified in the integration instance. (Default: False) | Optional |
| override_host | If specified, uses this host in place of the one specified in the instance configuration. | Optional |
| override_password | If specified, uses this password in place of the one specified in the instance configuration. | Optional |
| override_platform | If specified, uses this platform name in place of the one specified in the instance configuration. | Optional |
| override_port | If specified, uses this port in place of the one specified in the instance configuration. | Optional |
| override_username | If specified, uses this username in place of the one specified in the instance configuration. | Optional |
| raw_print | Prints the raw output directly to the war room (Default: False) | Optional |
| require_enable | Specifies whether or not the enable command must be executed before the commands specified in the cmds parameter. (Default: False) | Optional |
| require_exit | Specifies an optional command that must be executed upon completion of the cmds parameter being executed. (Default: False) | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Netmiko.Command | String | The executed command(s) |
| Netmiko.DateTimeUTC | DateTime | The datetime at which the command(s) were executed (in UTC) |
| Netmiko.Hostname | String | The hostname used for this execution of the integration |
| Netmiko.Output | String | The results of the command(s) that were executed |
Command Example (Single command)
!netmiko-cmds cmds="whoami"
Context Example
{
“Command”: “whoami”,
“DateTimeUTC”: “2023-04-24T21:40:21.755985”,
“Hostname”: “192.168.0.1”,
“Output”: “[someuser@someserver ~]$ root”
}
Human Readable Output
Command(s) against 192.168.0.1 (linux)
| Command | DateTimeUTC | Hostname | Output |
|---|---|---|---|
| whoami | 2023-04-24T21:40:21.755985 | 192.168.0.1 | root |
Command Example (Multiple commands)
As multiple commands via CLI or task
!netmiko-cmds cmds="whoami
who"
As multiple commands via CLI or task using an array
array context key = ["whoami", "who"]
!netmiko-cmds cmds=${array}
Context Example
{
“Netmiko”: [{
“Command”: “whoami”,
“DateTimeUTC”: “2023-04-24T21:59:02.177240”,
“Hostname”: “192.168.0.1”,
“Output”: “[someuser@somehost ~]$ root”
},
{
“Command”: “who”,
“DateTimeUTC”: “2023-04-24T21:59:04.882842”,
“Hostname”: “192.168.0.1”,
“Output”: “[someuser@somehost ~]$ root pts/0 2023-04-24 17:58 (192.168.0.1)”
}]
}
Human Readable Output
Command(s) against 192.168.0.1 (linux)
| Command | DateTimeUTC | Hostname | Output |
|---|---|---|---|
| whoami | 2023-04-24T21:59:02.177240 | 192.168.0.1 | root |
| who | 2023-04-24T21:59:04.882842 | 192.168.0.1 | [someuser@somehost ~]$ root pts/0 2023-04-24 17:58 (192.168.0.1) |
Configuration parameters
hostname— Hostname (required)port— Port (required)platform— Platform (required)credentials— Credentials (required)TimeoutOverride— Override the default timeout value
Commands (1)
-
netmiko-cmdsExecute commands using Netmiko.
import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 """ IMPORTS """ # Logging only needed for netmiko debugging # import logging import io import sys from datetime import datetime import paramiko from netmiko import ConnectHandler # value for Netmiko last_read parameter LAST_READ_TIMEOUT = 15.0 """ HELPER FUNCTIONS """ # Return only specific keys from dictionary def include_keys(dictionary, keys): key_set = set(keys) & set(dictionary.keys()) return {key: dictionary[key] for key in key_set} class Client: # pragma: no cover def __init__(self, platform, hostname, username, password, port, keys, timeout): self.platform = platform self.hostname = hostname self.username = username self.password = password self.timeout = int(timeout) self.port = port self.keys = keys self.net_connect = ConnectHandler def connect(self): if self.keys: try: self.net_connect = ConnectHandler( # type: ignore[assignment] device_type=self.platform, host=self.hostname, port=self.port, pkey=self.keys, username=self.username, read_timeout_override=self.timeout, ) except Exception as err: return_error(err) else: try: self.net_connect = ConnectHandler( # type: ignore[assignment] device_type=self.platform, host=self.hostname, port=self.port, use_keys=False, username=self.username, password=self.password, read_timeout_override=self.timeout, ) except Exception as err: return_error(err) def disconnect(self): try: if self.net_connect: # type: ignore[truthy-function] self.net_connect.disconnect() # type: ignore[attr-defined] except Exception as err: return_error(err) def cmds(self, require_exit, exit_argument, commands, enable, isConfig): try: output = {"Hostname": self.hostname, "Platform": self.platform, "Commands": []} self.connect() if enable: self.net_connect.enable() # type: ignore[attr-defined] if isConfig: output["Commands"].append( { "Hostname": self.hostname, "DateTimeUTC": datetime.utcnow().isoformat(), "Config": self.net_connect.send_config_set(commands, read_timeout=self.timeout), # type: ignore[attr-defined] } ) if not isConfig: for cmd in commands: prompt = self.net_connect.find_prompt() # type: ignore[attr-defined] pre_out = self.net_connect.send_command_timing( # type: ignore[attr-defined] cmd, read_timeout=self.timeout, strip_prompt=False, last_read=LAST_READ_TIMEOUT ) pattern_to_keep = re.escape(prompt) out = re.sub(pattern_to_keep, "", pre_out, count=len(re.findall(pattern_to_keep, pre_out))).strip() c = { "Hostname": self.hostname, "DateTimeUTC": datetime.utcnow().isoformat(), "Command": cmd, "Output": f"{prompt} {out}", } output["Commands"].append(c) except Exception as err: return_error(err) finally: self.disconnect() return output def test_command(client): # pragma: no cover client.connect() client.disconnect() demisto.results("ok") sys.exit(0) def cmds_command(client, args): # Parse the commands cmds = args.get("cmds") if not isinstance(cmds, list): # pragma: no cover try: cmds = cmds.split("\n") except Exception as err: return_error("The 'cmds' input needs to be a JSON array or carriage return (SHIFT+ENTER) separated" + f"text - {err}") cmds[:] = [x for x in cmds if len(x) > 0] # Parse the remaining arguments isConfig = args.get("isConfig", "false") == "true" enable = args.get("require_enable", "false") == "true" require_exit = args.get("require_exit", "false") == "true" exit_argument = args.get("exit_argument", None) raw_print = args.get("raw_print", "false") == "true" disable_context = args.get("disable_context", "false") == "true" override_host = args.get("override_host", None) override_port = args.get("override_port", None) override_platform = args.get("override_platform", None) override_username = args.get("override_username", None) override_password = args.get("override_password", None) client.hostname = override_host if override_host else client.hostname client.port = override_port if override_port else client.port client.platform = override_platform if override_platform else client.platform client.username = override_username if override_username else client.username client.password = override_password if override_password else client.password # Execute the commands output = client.cmds(require_exit, exit_argument, cmds, enable, isConfig) raw_print_list = [] # Output the results if raw_print: md = "" try: for command in output.get("Commands"): raw_print_list.append(command.get("Output")) md = "\n".join(raw_print_list) except Exception as err: md = "Error parsing raw print output" demisto.error(f"Error with raw print output - {err}") else: hdrs = ["Hostname", "DateTimeUTC", "Command", "Output"] data = [] # Single command if len(cmds) == 1: data.append(output["Commands"][0]) # Multiple commands else: for item in output["Commands"]: data.append(include_keys(item, hdrs)) md = tableToMarkdown(f"Command(s) against {client.hostname} ({client.platform}):", data, headers=hdrs) outputs_key_field = None outputs_prefix = None outputs = None if not disable_context: outputs_prefix = "Netmiko" outputs_key_field = "DateTimeUTC" outputs = output command_results = CommandResults( outputs_prefix=outputs_prefix, outputs_key_field=outputs_key_field, outputs=outputs, readable_output=md ) return command_results def main(): # pragma: no cover # Uncomment the logging.getLogger line to turn on netmiko debugging (shown in integration-instance.log when debug mode is on) # Be sure to uncomment the import logging command at the top of the integration # Helpful in troubleshooting incorrect command outputs from remote devices # logging.getLogger("netmiko").setLevel(logging.DEBUG) params = demisto.params() args = demisto.args() command = demisto.command() platform = params.get("platform") hostname = params.get("hostname") port = params.get("port") or "22" try: port = int(port) except Exception as err: return_error(f"Please ensure the port number is a number - {err}") username = params.get("credentials", {}).get("identifier") password = params.get("credentials", {}).get("password") ssh_key = params.get("credentials", {}).get("credentials", {}).get("sshkey") timeout = params.get("TimeoutOverride", 60) keys = None if ssh_key: if password: try: keys = paramiko.RSAKey.from_private_key(io.StringIO(ssh_key), password=password) except Exception as err: return_error(f"There was an error - {err} - Did you provide the correct password?") else: keys = paramiko.RSAKey.from_private_key(io.StringIO(ssh_key)) client = Client(platform, hostname, username, password, port, keys, timeout) if command == "test-module": test_command(client) elif command == "netmiko-cmds": results = cmds_command(client, args) return_results(results) if __name__ in ["__main__", "builtin", "builtins"]: # pragma: no cover main()