Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rough SSH Bastion Host Support (relates to #2144) #2150

Merged
merged 20 commits into from
Nov 2, 2015

Conversation

lattwood
Copy link
Contributor

Rudimentary testing was done with https://gist.github.com/lattwood/4c52ba7b8583ae4565eb, I'll be doing more extensive testing using st2workroom today. This touches on #2144

This is the most python I've written so I apologize in advance if anything isn't done the python way.

Is a CLA required or would Apache licensing suffice?

transport = self.bastion_client.get_transport()
real_addr = (self.hostname, self.port)
# no attempt is made to bind local_addr, so something very "wrong" is used
local_addr = ('256.256.256.256', 65536)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't 100% sure on what values to use here. Originally I had 127.0.0.1:1234, but since it doesn't actually bind the socket, I thought it made sense to use something that was completely outside of spec.

@manasdk
Copy link
Contributor

manasdk commented Oct 30, 2015

I am taking a look at this PR but we should wait for @lakshmi-kannan and @Kami.

@@ -216,6 +218,12 @@ def _execute_in_pool(self, execute_method, **kwargs):
return results

def _connect(self, host, results, raise_on_any_error=False):
bastion_hostname, bastion_port = None, None

if "!" in host:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typically breaking out magic string as a constant helps.

Probably place it somewhere https://github.com/StackStorm/st2/blob/master/st2common/st2common/constants/runners.py

local_addr = ('256.256.256.256', 65536)
channel = transport.open_channel("direct-tcpip", real_addr, local_addr)

conninfo = {'hostname': '256.256.256.256',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can use local_addr[0] and local_addr[1]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense from a logging perspective.

This made me realize the AutoAddPolicy the ParamikoSSH client uses will end up erroring if you have two hosts with the same IP behind the bastion host- I'll change the policy to WarningPolicy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: nevermind, it appears as though it's not saved on disk as no host_keys_file is specified on the clients.

@manasdk
Copy link
Contributor

manasdk commented Oct 30, 2015

High level this is great. most of my comments are really cosmetic things.

Few thoughts -

  1. Instead of overriding the hosts property perhaps a bastion_host property would make sense on the runner?
  2. Perhaps a special runner make more sense and keep things explicit for users. This could be a consumption hinderance so maybe this is not such a good idea.

@manasdk
Copy link
Contributor

manasdk commented Oct 30, 2015

Also, perhaps a good idea to add some units -

https://github.com/StackStorm/st2/blob/master/st2actions/tests/unit/test_parallel_ssh.py

@lattwood
Copy link
Contributor Author

I was thinking about a bastion_host property, but my understanding is that it would require the modification of any actions using it to add the bastion_host(s) parameter. I would like to make it explicit, but that's a large drawback.

If there is a way to implicitly add the bastion_host(s) parameter to all actions supporting the remote-shell-cmd, how do you run a single command on mixed groups of hosts? (ie, how do you indicate hosts a,b,c are behind bastion1, d,e,f are behind bastion2, and g,h are directly connectable)

@manasdk
Copy link
Contributor

manasdk commented Oct 30, 2015

but my understanding is that it would require the modification of any actions using it to add the bastion_host(s) parameter.

Actually if we make bastion_host(s) an optional runner_parameter then all actions get it for free.

Concretely adding another property like -
https://github.com/StackStorm/st2/blob/master/st2common/st2common/bootstrap/runnersregistrar.py#L117
https://github.com/StackStorm/st2/blob/master/st2common/st2common/bootstrap/runnersregistrar.py#L203

how do you run a single command on mixed groups of hosts?

This is a tricky one. Unless we come up with a host_group concept this will not work very nicely. For the near term I would suggest the for each execution we only allow 1 bastion host i.e. all hosts for that execution would be assumed to be behind the same bastion.

To run against multiple groups would require multiple executions. If practically it turns out to be the common case then we can look at options as the next step.

@lattwood
Copy link
Contributor Author

lattwood commented Nov 1, 2015

@manasdk much cleaner this way, just need to figure out unit tests

@@ -111,6 +111,11 @@ def __init__(self, hostname, port=22, username=None, password=None,
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.logger = logging.getLogger(__name__)
self.sftp = None
self.bastion_host = bastion_host
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self._bastion_host. Can you also use the _ convention everywhere consistently?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't use _bastion_host as the other properties on ParamikoSSHClient aren't prefixed.

The SSH connection is established when connect is called
this requires mocking SSHClient for those methods
local_addr = ('', 0)
self.bastion_socket = transport.open_channel('direct-tcpip', real_addr, local_addr)

self.client = self._connect(self.hostname, self.bastion_socket)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self._connect(host=self.hostname, socket=self.bastion_socket) would be better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do the same on line 127 as well.

self.bastion_client = self._connect(self.bastion_host)
transport = self.bastion_client.get_transport()
real_addr = (self.hostname, self.port)
local_addr = ('', 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not too much hassle, could you please include the link to the fabric commit as a link? This might throw off other people when they read the code. Some helpful hinting would be nice IMO.

@lakshmi-kannan
Copy link
Contributor

It's looking much better now in terms of code and tests.

One other thing I thought was if bastion host had a non standard SSH port, we'll need to add ability to handle that. But we'll make the person requiring the feature to add that ;).

Thanks for being patient. We'll merge this soon!

@lattwood
Copy link
Contributor Author

lattwood commented Nov 2, 2015

@lakshmi-kannan something something security something obscurity :)

@lakshmi-kannan
Copy link
Contributor

LGTM. Merging now.

lakshmi-kannan added a commit that referenced this pull request Nov 2, 2015
Rough SSH Bastion Host Support (relates to #2144)
@lakshmi-kannan lakshmi-kannan merged commit a75b103 into StackStorm:master Nov 2, 2015
@saisandeepachanta
Copy link

@lakshmi-kannan or @lattwood I has started using stack storm recently. My environment is accessed by only one jump box. My stack storm is installed on a new box where i have passwordless connectivity to jump server. i have to use my jump server as bastion host and then trigger my scripts on target hosts. can you please post me some examples how to use or configure the bastion host details in stackstorm. i desperately need your help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants