Skip to content

Instantly share code, notes, and snippets.

@tintoy
Last active April 27, 2018 05:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tintoy/a6ca69047b0a1158f74fbba764f2ce79 to your computer and use it in GitHub Desktop.
Save tintoy/a6ca69047b0a1158f74fbba764f2ce79 to your computer and use it in GitHub Desktop.
StackStorm action to execute a command over a chain of SSH hosts
#! /usr/bin/env python
import paramiko
import pprint
from st2common.runners.base_action import Action
from StringIO import StringIO
class JumpboxExec(Action):
def run(self, command, hosts):
ssh_connection = build_host_chain(hosts)
(_, stdout, stderr) = ssh_connection.exec_command(command)
return (True, {
"stdout": stdout.read().strip(),
"stderr": stderr.read().strip()
})
def build_host_chain(hosts):
hosts.reverse()
tunnel = None
while hosts:
host = hosts.pop()
target_ipv4 = host['target_ipv4']
local_ipv4 = host.get('local_ipv4', target_ipv4)
user = host['user']
key = paramiko.RSAKey(
file_obj=StringIO(host['key'])
)
print("Connect to host {0} (local_ipv4={1}) as {2} (tunneled={3})...".format(
target_ipv4,
local_ipv4,
user,
len(hosts) > 0
))
connection = paramiko.SSHClient()
connection.set_missing_host_key_policy(
paramiko.AutoAddPolicy()
)
if hosts:
connection.connect(target_ipv4, username=user, pkey=key, sock=tunnel)
(_, stdout, _) = connection.exec_command('hostname')
print("Connected to host {0} ({1}).".format(
target_ipv4,
stdout.read().strip()
))
# Tunnel through to the next host
next_host = hosts[len(hosts) - 1]
next_target_ipv4 = next_host['target_ipv4']
src_addr = (local_ipv4, 22)
dest_addr = (next_target_ipv4, 22)
print("Open tunnel from {0} to {1}...".format(
local_ipv4,
next_target_ipv4
))
tunnel = connection.get_transport().open_channel("direct-tcpip", dest_addr, src_addr)
print("Tunnel connected.")
else:
connection.connect(target_ipv4, username=user, pkey=key, sock=tunnel)
(_, stdout, _) = connection.exec_command('hostname')
print("Connected to host {0} ({1}).".format(
target_ipv4,
stdout.read().strip()
))
return connection
---
name: "jumpbox-exec"
runner_type: "python-script"
description: "Execute commands on a remote system using one or more jumpbox hosts."
enabled: true
entry_point: "jumpbox-exec.py"
parameters:
command:
type: "string"
description: "The commands to execute."
required: true
position: 0
hosts:
type: "array"
description: "Details for the target host and any jumpbox hosts used to connect to it (the target host is the last entry)."
required: true
secret: true
position: 1
items:
type: "object"
properties:
target_ipv4:
type: "string"
description: "The host's IPV4 address."
required: true
tunnel_local_ipv4:
type: "string"
description: "The host's local IPV4 address (used when creating the local end of this hop of the SSH tunnel). Can be omitted if it's the same as target_ipv4."
required: false
user:
type: "string"
description: "The user name for SSH authentication."
required: true
key:
type: "string"
description: "The private key for SSH authentication."
required: true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment