-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement export and import commands for ssh conf.
- Loading branch information
Showing
21 changed files
with
352 additions
and
208 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Module with CLI command to export and import hosts.""" | ||
from termius.core.storage.strategies import ( | ||
RelatedSaveStrategy, RelatedGetStrategy | ||
) | ||
|
||
from termius.porting.providers.ssh.provider import SSHPortingProvider | ||
|
||
from ..core.commands import AbstractCommand | ||
|
||
|
||
class SSHImportCommand(AbstractCommand): | ||
"""Import hosts from user`s ssh config.""" | ||
|
||
save_strategy = RelatedSaveStrategy | ||
get_strategy = RelatedGetStrategy | ||
|
||
def take_action(self, parsed_args): | ||
"""Process CLI call.""" | ||
provider = SSHPortingProvider(storage=self.storage, crendetial=None) | ||
provider.import_hosts() | ||
|
||
self.log.info('Import hosts from ~/.ssh/config to local storage.') | ||
|
||
|
||
class SSHExportCommand(AbstractCommand): | ||
"""Export hosts from local storage to generated file.""" | ||
|
||
get_strategy = RelatedGetStrategy | ||
|
||
def take_action(self, parsed_args): | ||
"""Process CLI call.""" | ||
provider = SSHPortingProvider(storage=self.storage, crendetial=None) | ||
provider.export_hosts() | ||
|
||
self.log.info( | ||
'Export hosts from local storage to ~/.termius/sshconfig' | ||
) |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Package with logic to import and export hosts from ssh config provider.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Module with adapter for ssh config hosts.""" | ||
from os import environ | ||
|
||
from pathlib2 import Path | ||
|
||
from termius.core.commands.mixins import SshConfigMergerMixin | ||
from termius.core.models.terminal import Host, SshConfig, Identity, SshKey | ||
|
||
|
||
class SSHConfigHostAdapter(SshConfigMergerMixin): | ||
"""Class for adapting app host and ssh config hosts.""" | ||
|
||
default_user = environ.get('USER', None) | ||
|
||
def get_instance_ssh_key_label(self, ssh_config): | ||
"""Helper to retrieve ssh_key lable.""" | ||
if ssh_config['identity'] and ssh_config['identity']['ssh_key']: | ||
return ssh_config['identity']['ssh_key']['label'] | ||
|
||
return None | ||
|
||
def create_key(self, config): | ||
"""Construct new application ssh key instance.""" | ||
if 'identityfile' not in config: | ||
return None | ||
identityfile = self.choose_ssh_key(config['identityfile'], config) | ||
if not identityfile: | ||
return None | ||
content = identityfile.read_text() | ||
return SshKey(label=identityfile.name, private_key=content) | ||
|
||
# pylint: disable=unused-argument,no-self-use | ||
def choose_ssh_key(self, sshkeys, host_config): | ||
"""Choose single ssh key path instance from ones.""" | ||
key_paths = [Path(i) for i in sshkeys] | ||
existed_paths = [i for i in key_paths if i.is_file()] | ||
return existed_paths and existed_paths[0] | ||
|
||
def adapt_instance_to_ssh_config_host(self, host_instance): | ||
"""Convert app host to ssh config host.""" | ||
host_instance.ssh_config.identity.is_visible = True | ||
ssh_config = self.get_merged_ssh_config(host_instance) | ||
|
||
adapted = { | ||
'hostname': host_instance['address'], | ||
'user': ssh_config['identity']['username'], | ||
'port': ssh_config['port'] or 22 | ||
} | ||
|
||
host_key_label = self.get_instance_ssh_key_label(ssh_config) | ||
|
||
if host_key_label: | ||
adapted.update( | ||
identityfile='~/.termius/ssh_keys/' + host_key_label | ||
) | ||
|
||
return adapted | ||
|
||
def adapt_ssh_config_host_to_instance(self, alias, parsed_host): | ||
"""Convert parsed host to application host.""" | ||
app_host = Host( | ||
label=alias, | ||
address=parsed_host['hostname'], | ||
) | ||
ssh_config = SshConfig( | ||
identity=Identity( | ||
username=parsed_host.get('user', self.default_user), | ||
ssh_key=self.create_key(parsed_host) | ||
) | ||
) | ||
|
||
ssh_config.port = parsed_host.get('port') | ||
ssh_config.timeout = parsed_host.get('serveraliveinterval') | ||
ssh_config.keep_alive_packages = parsed_host.get('serveralivecountmax') | ||
ssh_config.use_ssh_key = parsed_host.get('identitiesonly') | ||
ssh_config.strict_host_key_check = parsed_host.get( | ||
'stricthostkeychecking' | ||
) | ||
|
||
app_host.ssh_config = ssh_config | ||
|
||
return app_host |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Module with ssh config parser.""" | ||
|
||
import re | ||
from paramiko.config import SSHConfig | ||
|
||
|
||
class SSHConfigParser(SSHConfig): | ||
"""Class to override parse method of paramiko parser.""" | ||
|
||
def parse(self, file_obj): # noqa | ||
""" | ||
Read an OpenSSH config from the given file object. | ||
:param file_obj: a file-like object to read the config file from | ||
""" | ||
termius_ignore_regexp = re.compile(r'# termius:ignore') | ||
|
||
host = {'host': ['*'], 'config': {}} | ||
|
||
for line in file_obj: | ||
line = line.strip() | ||
|
||
if not line: | ||
continue | ||
|
||
if line.startswith('#'): | ||
ignore_comment = termius_ignore_regexp.match(line) | ||
|
||
if ignore_comment: | ||
host['config']['ignore'] = '' | ||
|
||
continue | ||
|
||
match = re.match(self.SETTINGS_REGEX, line) | ||
if not match: | ||
raise Exception('Unparsable line %s' % line) | ||
key = match.group(1).lower() | ||
value = match.group(2) | ||
if key == 'host': | ||
self._config.append(host) | ||
host = { | ||
'host': self._get_hosts(value), | ||
'config': {} | ||
} | ||
|
||
elif key == 'proxycommand' and value.lower() == 'none': | ||
host['config'][key] = None | ||
else: | ||
if value.startswith('"') and value.endswith('"'): | ||
value = value[1:-1] | ||
if key in ['identityfile', 'localforward', 'remoteforward']: | ||
if key in host['config']: | ||
host['config'][key].append(value) | ||
else: | ||
host['config'][key] = [value] | ||
elif key not in host['config']: | ||
host['config'][key] = value | ||
|
||
self._config.append(host) |
Oops, something went wrong.