Skip to content

Added support for parsing 'Include' directive in ssh config file.#872

Closed
FDT2k wants to merge 3 commits intoparamiko:mainfrom
FDT2k:master
Closed

Added support for parsing 'Include' directive in ssh config file.#872
FDT2k wants to merge 3 commits intoparamiko:mainfrom
FDT2k:master

Conversation

@FDT2k
Copy link
Copy Markdown

@FDT2k FDT2k commented Jan 8, 2017

Just added support for parsing the Include directive in ssh config file.

@coveralls
Copy link
Copy Markdown

Coverage Status

Coverage decreased (-0.2%) to 74.013% when pulling 9d183fe on FDT2k:master into d6d54df on paramiko:master.

3 similar comments
@coveralls
Copy link
Copy Markdown

Coverage Status

Coverage decreased (-0.2%) to 74.013% when pulling 9d183fe on FDT2k:master into d6d54df on paramiko:master.

@coveralls
Copy link
Copy Markdown

Coverage Status

Coverage decreased (-0.2%) to 74.013% when pulling 9d183fe on FDT2k:master into d6d54df on paramiko:master.

@coveralls
Copy link
Copy Markdown

Coverage Status

Coverage decreased (-0.2%) to 74.013% when pulling 9d183fe on FDT2k:master into d6d54df on paramiko:master.

self._config = []

def parse(self, file_obj):
def parse(self, file_obj,parsed_files=None):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

style nit: space after comma


path = value
# according to SSH documentation if the path is relative, we look forward in ~/.ssh
if path[0] != '/' and path[0]!='~':
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

style nit: spaces around !=

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

you may want to use os.path.isabs() for the first part of this condition, to perhaps enable better compatibility with windows

raise Exception("Include loop detected in ssh config file: %s" %path)
with open(filename) as fd:
parsed_files.append(path);
self.parse(fd,parsed_files)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

more minor style nits - space after comma, no semicolon at end of the previous line, spaces around % three lines up

@ploxiln
Copy link
Copy Markdown
Contributor

ploxiln commented Jan 8, 2017

Overall looks good to me.

@coveralls
Copy link
Copy Markdown

coveralls commented Jan 9, 2017

Coverage Status

Coverage decreased (-0.1%) to 74.082% when pulling 4c97102 on FDT2k:master into d6d54df on paramiko:master.

@coveralls
Copy link
Copy Markdown

coveralls commented Jan 9, 2017

Coverage Status

Coverage decreased (-0.3%) to 73.903% when pulling a225bc8 on FDT2k:master into d6d54df on paramiko:master.

@coveralls
Copy link
Copy Markdown

coveralls commented Jan 9, 2017

Coverage Status

Coverage decreased (-0.1%) to 74.082% when pulling a225bc8 on FDT2k:master into d6d54df on paramiko:master.

@coveralls
Copy link
Copy Markdown

Coverage Status

Coverage decreased (-0.1%) to 74.041% when pulling a225bc8 on FDT2k:master into d6d54df on paramiko:master.

@ploxiln
Copy link
Copy Markdown
Contributor

ploxiln commented Feb 24, 2017

Worth documenting here that this is a new feature in ssh 7.3p1, and this PR does seem to reproduce it pretty closely.

 Include
         Include the specified configuration file(s).  Mul‐
         tiple pathnames may be specified and each pathname
         may contain glob(3) wildcards and, for user config‐
         urations, shell-like “~” references to user home
         directories.  Files without absolute paths are
         assumed to be in ~/.ssh if included in a user con‐
         figuration file or /etc/ssh if included from the
         system configuration file.  Include directive may
         appear inside a Match or Host block to perform con‐
         ditional inclusion.

for filename in glob.iglob(path):
if os.path.isfile(filename):
if filename in parsed_files:
raise Exception("Include loop detected in ssh config file: %s" % filename)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should be some more specific exception class, using just Exception is almost never useful (it's impossible to catch without catching everything else too!); not sure what works best though. Maybe ValueError, or a new custom exception class (meh).

@bitprophet
Copy link
Copy Markdown
Member

This is a comparatively easy part of the code to test so I'd love it if tests could be added.

@bitprophet
Copy link
Copy Markdown
Member

bitprophet commented Mar 1, 2017

Was gonna drop this as a line comment but not sure I trust github to display it even after the code is modified, and it got complex enough to be a permanent part of the discussion. It's re: the treatment of the file path stuff on line 87:

That's not how I'd interpreting the current OpenSSH docs...it's just stating that it allows normal shell-like globbing and user home directory references (e.g. ~/some-dir/some_file.conf expands to be /home/myusername/some-dir/some_file.conf - not ~/.ssh/some-dir/some_file.conf).

What they mean by "assuming ~/.ssh" is simply that the implicit relative location is ~/.ssh, so Include foo means Include ~/.ssh/foo:

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.

Also, that brings up another wrinkle, namely the detection of whether the current file being parsed is a "user" or "system" configuration file (and they do not even mention how it is expected to behave if it is a runtime-specified file...). Need to think about that, I could see a few different ways to handle it:

  • Look at the file path of the file object, if possible, and if it appears to be a user or system file, interpret relative paths as documented
  • Simply grab the os.path.dirname of that file path and use it as the relative root; this automatically works for regular user/system conf files, but is possibly more poorly defined for runtime files and especially files loaded via Include itself (!).
    • At the same time, this feels like a relatively intuitive behavior to me - that a conf file include would be relative to where the including file is located.
    • But generally, especially given this feature area's whole point, we want to ape OpenSSH's behavior as closely as possible...so maybe source diving is needed.
  • The specific-to-Python case of the file object being unrelated to a real file (i.e. a StringIO or other FLO) is poorly defined here, but I reckon somebody expecting to use a relative-path Include directive in such a situation is probably just asking for trouble...

@tescalada
Copy link
Copy Markdown

I think the relative locations handling here is correct.

My ssh config uses Include config.d/*
which then expands to parse out /Users/tristan/.ssh/config.d/*

I tried adding Include ~/sshtest
which correctly parsed the file in /Users/tristan/sshtest

@tescalada
Copy link
Copy Markdown

I would be happy to help out by adding some unit testing here, but it does not seem as straight forward to add the tests as it is for the other config tests. I would either need to add mock as a requirement to the project, or drop some test config files into the repo to be able to parse finding and reading the actual files (although I would not be able to test ~ this way).

Do you have a preference on those two options, or a better option I didn't think of?

@brycefisher
Copy link
Copy Markdown

brycefisher commented Aug 24, 2017

This PR makes me super happy!! I'd love to see this get merged in the nearish future and I'm happy to help contribute some tests if @FDT2k would be amenable to PRs against this PR :-)

It seems like the work that needs doing here is:

  • Add tests somehow... Would @ploxiln / @bitprophet or others object to introducing mock as a dependency into this project to help with testing things like tilde expansion?
  • Get consensus on how to handle relative file paths between. It appears the current implementation in this PR would expand:
    • Include foo -> $HOME/.ssh/foo
    • Include ~/foo -> $HOME/foo
    • Include /foo -> /foo
      To me that seems right as is. @bitprophet should that be something else?
  • Decide whether or not check if the file is a system or user configuration. My opinion is that we ought to skip that check and revisit this design if it causes issues for users in the future. If we really wanted to do this check, then we could look at what OpenSSH does -- but again, I would just do the tilde expansion regardless to keep the implementation simple and portable.
  • Raise a more specific exception in the case of a file inclusion loop. I have no opinion on what a better one would be, but I agree more specific than Exception would be great. I'd be happy to provide ValueError instead.

@brycefisher
Copy link
Copy Markdown

@bitprophet @ploxiln looks like both @tescalada and I are willing to help get this PR over the finish 🏆

I just wanted to say thanks for maintaining this awesome project and all the love and attention you've put into this. Whenever you all have a minute, we'd love to get your two cents on the items @tescalada and I asked about. Thanks again!!!

@haakonn
Copy link
Copy Markdown

haakonn commented Dec 6, 2017

This doesn't support globs, such as this:

Include config.d/*.conf

This leads to Exception: Include loop detected in ssh config file: /home/username/.ssh/config.d/*.conf (but there is no include loop).

@PMickael
Copy link
Copy Markdown

How about a merge ? 🙏

@humiaozuzu
Copy link
Copy Markdown

Any update on this PR?

@ghost
Copy link
Copy Markdown

ghost commented Apr 22, 2018

Rebased against current master, works fine.

@bitprophet

That's not how I'd interpreting the current OpenSSH docs...it's just stating that it allows normal
shell-like globbing and user home directory references (e.g. ~/some-dir/some_file.conf expands
to be /home/myusername/some-dir/some_file.conf - not ~/.ssh/some-dir/some_file.conf).

Original author @FDT2k implementation is correct : I compared file expansion with native openssh client on Debian stable.

Also, that brings up another wrinkle, namely the detection of whether the current file being parsed
is a "user" or "system" configuration file (and they do not even mention how it is expected to behave
if it is a runtime-specified file...)

Good catch, please see #1194.

@haakonn

This doesn't support globs, such as this:
Include config.d/*.conf

I cannot reproduce your issue : importing patterns with wildcards patterns work fine here. It seems you are trying to import a file multiple times. (see 9d183fe#diff-a58d9daf5b4cbdffb4dee739593a7813R101)

@danihodovic
Copy link
Copy Markdown

How about a merge ? 🙏

@bitprophet bitprophet added this to the p1 milestone Dec 23, 2020
@guenhter
Copy link
Copy Markdown

@FDT2k this is such a useful feature. Would you mind working in the one comment of the review.

@jsiverskog
Copy link
Copy Markdown

i cleaned this up and added a test (#1892)

@anarcat
Copy link
Copy Markdown

anarcat commented Apr 4, 2023

this PR should be closed in favor of #1892.

@bitprophet
Copy link
Copy Markdown
Member

Yup, rolling into #1892

@bitprophet bitprophet closed this May 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.