haginara / ssh_config Goto Github PK
View Code? Open in Web Editor NEWSSH Client config file manager
License: MIT License
SSH Client config file manager
License: MIT License
I want to put this as bash aliases, but I don't see any option to omit interactive question.
$ ssh-config add "cvps" HostName=192.168.140.1 Port=22 User=chris IdentityFile=""
Host cvps
HostName 192.168.140.1
User chris
Port 22
Information is correct ? [y/N]: y
Added!
$
You should add --yes
so one don't have to manually press y. Thanks ! Great program overall
SSH_CONFIG supports defining multiple host patterns:
Host pattern1 pattern2
IdentityFile file
But parsing this at the moment fails with an issue in pyparsing.
Hello @haginara
Thanks for this useful lib.
I just found out that ssh_config
does not support global options at the beginning of the file and unindented block ( indenting a block is always a good practice, but it's not required in the ssh config file ).
Here a dummy example of config file which leads to a WrongSSHConfig
:
CanonicalizeHostname no
CanonicalDomains no
Host github.com
User git
IdentityFile ~/.ssh/id_github
or simply
Host github.com
User git
IdentityFile ~/.ssh/id_github
But both files have a "good" syntax.
I tried to modify the pattern defined with pyparsing
in order to parse the unindented global options, but it only works partially :
def parse(self, data=""):
"""Parse ssh-config data
args:
data (str)
returns:
Parsed config or None (dict)
"""
if data:
self.raw = data
SPACE = White().suppress()
SEP = Suppress(SPACE) | Suppress("=")
HOST = CaselessLiteral("Host").suppress()
MATCH = CaselessLiteral("Match").suppress()
KEY = Word(alphanums)
VALUE = Word(alphanums + ' ~%*?!._-+/,"')
paramValueDef = SkipTo("#" | lineEnd)
indentStack = [1]
HostDecl = (HOST | MATCH) + SEP + VALUE
paramDef = Dict(Group(~HOST + KEY + SEP + paramValueDef))
block = indentedBlock(paramDef, indentStack)
HostBlock = Dict(Group(HostDecl + block))
Begin = Empty().setParseAction(replaceWith("GLOBAL_OPTIONS"))
GlobalOptions = Group(ZeroOrMore(Dict(Group(paramDef))))
try:
return (Begin + GlobalOptions + OneOrMore(HostBlock)).ignore(pythonStyleComment).parseString(self.raw)
except ParseException as e:
print(e)
return None
The idea is to parse an eventual first unindented block without host definition, and add the keyword "GLOBAL_OPTIONS"
to this block in order to have a proper dict.
It seems to work with the parsed object, but not for the conversion .asDict()
:
# parsed :
['GLOBAL_OPTIONS', [[['CanonicalizeHostname', 'no']], [['CanonialDomains', 'no']]], ['github.com', [[['User', 'git']], [['IdentityFile', '~/.ssh/id_github']]]]]
# parsed.asDict() :
{'github.com': [{'User': 'git'}, {'IdentityFile': '~/.ssh/id_github'}]}
The block "GLOBAL_OPTIONS"
appears correctly in the object, but is not converted in the resulted dict.
Since I don't know the lib pyparsing
very well, there's maybe an easier way to achieve this, and maybe you have a better idea for this, it would be great.
Thanks
Arnaud
It would be nice if this could recursively include files listed in the config file
From 7.3p1 and up, there is the Include keyword
Include
Include the specified configuration file(s). Multiple
pathnames may be specified and each pathname may contain
glob(7) wildcards and, for user configurations, shell-like
‘~’ references to user home directories. Wildcards will be
expanded and processed in lexical order. Files without
absolute paths are assumed to be in ~/.ssh if included in a
user configuration file or /etc/ssh if included from the
system configuration file. Include directive may appear
inside a Match or Host block to perform conditional
inclusion.
Source: ssh_config(5).
I tried reading through the source, but it is somewhat confusing.
On running:
ssh-config update
Traceback (most recent call last):
File "/miniconda3/envs/b37/bin/ssh-config", line 10, in <module>
sys.exit(main())
File "/miniconda3/envs/b37/lib/python3.7/site-packages/ssh_config/cli.py", line 98, in main
argv[1:], options_first=True, version="ssh_config %s" % __version__
File "/miniconda3/envs/b37/lib/python3.7/site-packages/ssh_config/cli.py", line 72, in __init__
command_cls = getattr(commands, command_name)
AttributeError: module 'ssh_config.commands' has no attribute 'Update'
There seems to also be no docs and no -h
command for subcommands.
It is better to change the line at
ssh_config/ssh_config/keywords.py
Line 19 in be893bc
to the following. Otherwise, "No" values would run into KeyError: 'No'
errors.
convert[value.lower()]
I suggest the code to look like
def yes_or_no(value: str) -> bool:
"""Convert 'yes' or 'no' to True or False
Args:
value (str): The string containing 'yes' or 'no'
Returns:
bool: True if value is 'yes', False if value is 'no'
"""
if value is None:
return
if value.lower() not in ('yes', 'no', 'true', 'false'):
raise TypeError(f"Yes or No is required: {value}")
convert = {
"yes": True,
"no": False,
"true": True,
"false": False,
True: "yes",
False: "no",
}
return convert[value.lower()]
Otherwise, I would run into errors when reading back the updated config file.
after installing with pip3 install ssh-config, running ssh-config produces error in title
simple change in /usr/local/bin/ssh-config ie. switching ssh_config.cli2 to ssh_config.cli makes utility run without error
Please besides ProxyCommand, ProxyJump can be added? It's usefull in other different cases, thanks!
If I parse a file like:
Host myhost
hostname myhost.mydomain.com
The parsed hosts gets the attribute HostName = None. However SSH_CONFIG(5) indicates that keywords are case insensitive.
Versions:
python: 2.7.16
pip: 19.1.1
pip install ssh-config
errror:
Using cached https://files.pythonhosted.org/packages/b3/a2/72ca9d7c0b181b58d462647421ba1531ce3a62ca9b1c8b65c29c3a0349b9/ssh_config-0.0.12.tar.gz
ERROR: Complete output from command python setup.py egg_info:
ERROR: Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/private/var/folders/2z/k2bsyg654t3bky0c96fntfj80000gn/T/pip-install-_NfBMw/ssh-config/setup.py", line 7, in <module>
from ssh_config import __version__
File "ssh_config/__init__.py", line 1, in <module>
from .client import SSHConfig, Host, EmptySSHConfig
File "ssh_config/client.py", line 5, in <module>
from pyparsing import (
ImportError: No module named pyparsing
----------------------------------------
ERROR: Command "python setup.py egg_info" failed with error code 1 in /private/var/folders/2z/k2bsyg654t3bky0c96fntfj80000gn/T/pip-install-_NfBMw/ssh-config/
It appears that this import: https://github.com/haginara/ssh_config/blob/develop/setup.py#L7
is causing the import of client.py
which requires pyparsing
which is not yet installed in a new environment.
Currently it has only import csv file feature. When we create a list of servers for other tools like a ansible, it's helpful to get list from ssh config.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.