19

It seems like the following would work as expected, i.e., that the second rule, having a hostname that matches the first rule, would apply it.

Host *.hostname.com
 User myuser
 IdentityFile ~/.ssh/myidentity

Host blah
 HostName complicated.hostname.com

However typing ssh blah only applies the second rule (and not the user or identity file of the first one).

I have two questions:

  1. Why is this happening?
  2. Is it possible to (simply) do what I am trying to do?

3 Answers 3

13

From the ssh_config man page:

For each parameter, the first obtained value will be used. The configuration files contain sections separated by “Host” specifications, and that section is only applied for hosts that match one of the patterns given in the specification. The matched host name is the one given on the command line.

Since the first obtained value for each parameter is used, more host-specific declarations should be given near the beginning of the file, and general defaults at the end.

Additionally I'd make sure I understand these 2 sections if you're unclear how the Host and PATTERNS function. There is only 1 level of matching going on. This facility is very basic in its regex capabilities, but is still powerful once you grok it.

Host sections

 The possible keywords and their meanings are as follows (note that keywords 
 are case-insensitive and arguments are case-sensitive):

 Host    Restricts the following declarations (up to the next Host keyword) 
         to be only for those hosts that match one of the patterns given
         after the keyword.  If more than one pattern is provided, they 
         should be separated by whitespace.  A single ‘*’ as a pattern can 
         be used to provide global defaults for all hosts.  The host is the 
         hostname argument given on the command line (i.e. the name is not
         converted to a canonicalized host name before matching).

         A pattern entry may be negated by prefixing it with an exclamation 
         mark (‘!’).  If a negated entry is matched, then the Host entry is      
         ignored, regardless of whether any other patterns on the line 
         match.  Negated matches are therefore useful to provide exceptions 
         for wildcard matches.

         See PATTERNS for more information on patterns.

PATTERNS

 A pattern consists of zero or more non-whitespace characters, ‘*’ (a 
 wildcard that matches zero or more characters), or ‘?’ (a wildcard that
 matches exactly one character).  For example, to specify a set of 
 declarations for any host in the “.co.uk” set of domains, the following
 pattern could be used:

       Host *.co.uk

 The following pattern would match any host in the 192.168.0.[0-9] network 
 range:

       Host 192.168.0.?

 A pattern-list is a comma-separated list of patterns.  Patterns within 
 pattern-lists may be negated by preceding them with an exclamation
 mark (‘!’).  For example, to allow a key to be used from anywhere within an 
 organisation except from the “dialup” pool, the following entry
 (in authorized_keys) could be used:

       from="!*.dialup.example.com,*.example.com"

Layering rules

The problem with your approach is that the pattern that matches the 1st Host section doesn't match the 2nd. I typically do something like this:

Host *
 User myuser
 IdentityFile ~/.ssh/myidentity


Host blah
 HostName complicated.hostname.com

One thing that people don't usually pick up on with these rules is that they can repeat. So what I often times do is have multiple sections and I break them up using Host *'s.

Host *
 User user1

Host blah1
 HostName complicated1.hostname.com

Host blah2
 HostName complicated2.hostname.com

Host *
 User user2
4
  • 5
    In your example, how is "user2" ever set? I thought the first obtained value for a host is used, so every host will match the first block and have "user1" set?
    – jdm
    Mar 10, 2014 at 11:11
  • @jdm - Host rules that come after the 2nd Host * which are matched will be using user2 as their default user, unless they explicitly specify it themselves.
    – slm
    Mar 10, 2014 at 11:45
  • 1
    @slm: I have actually found that not to work. If you chain two Host * User xxx and then Host * User yyy the next rule will be using "xxx" - unless I am doing something wrong.
    – Jérémie
    Sep 5, 2014 at 17:20
  • 1
    @slm, your example doesn't work. At the time the 2nd Host * is reached the 'first-obtained-value-for-each-parameter-is-used'-rule applies and thus this and all following User definitions are ignored. An exception from this rule are IdentityFile keywords, btw. Oct 3, 2016 at 21:59
7

SSH applies all the sections that match the hostname as provided on the command line (ie. the HostName rules it encounters do not affect subsequent conditions checks). If CanonicalizeHostname is enabled, it will reapply the configuration files again when it is finished, using the updated hostname. (Some SSH versions did this regardless of CanonicalizeHostname and your example would work with those versions; but that's considered a bug by the SSH devs. See #2267.)

Which means you can use CanonicalizeHostname to make your example work, by adding

Host *
  CanonicalizeHostname yes
  CanonicalizeFallbackLocal no

which will not do any canonicalization but will enable doing a second pass with the updated hostname. (Note that it will still not make the configuration parsing "recursive", just repeat it once. So if you change the hostname twice, that wouldn't work.)

3
  • 1
    I recently upgraded Ubuntu 14.04 to 16.04, and with it came this bug. This answer is perfect; it gets me right back to the original behavior. Thanks! Sep 7, 2016 at 22:33
  • ugh #2267 means that Host nickname; Hostname hostname stanzas are no longer able to provide nicknaming. It'll work if you add the CanonizalizeHostname yes keyword in each nickname block, but that doubles the size of the nickname blocks and looks ugly.
    – studog
    May 23, 2018 at 16:48
  • If you're using a bunch of nicknames for the same site, you can list them all in the Host or Match host test, then use %h in the HostName. For example, for foo.x.com and bar.x.com: Match host foo,bar CanonicalizeHostname yes Hostname %h.x.com Jul 2, 2020 at 20:11
1

From the man page

For each parameter, the first obtained value will be used. The configuration files contain sections separated by ''Host'' specifications, and that section is only applied for hosts that match one of the patterns given in the specification. The matched host name is the one given on the command line.

Since the first obtained value for each parameter is used, more host-specific declarations should be given near the beginning of the file, and general defaults at the end.

Try switching the order of your entries.

2
  • Unfortunately switching the order of the entries does not work (it was actually the order I had originally used).
    – Jérémie
    Feb 20, 2014 at 2:03
  • If there are multiple Host definition that match the hostname you're connecting to, all the parameters defined in all of them will be merged into a single definition. When it says "the first obtained value" it's talking about things at the parameter-level, not the Host-level. This is counter-intuitive if you're thinking of each Host block as being a definition.
    – anon
    Dec 12, 2015 at 15:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .