5

I wanted to use CanonicalizeHostname in my ssh config which would make it possible to add and remove hosts without having to edit the file.

Host bastion
    ProxyJump none

Match canonical
    ProxyJump bastion
    ForwardAgent yes

Host *
    ForwardAgent no
    CanonicalizeHostname always
    CanonicalDomains mydomain.co.uk
    CanonicalizeMaxDots 0
    CanonicalizeFallbackLocal yes

ssh-agent was not being forwarded to any of my servers. If I changed ForwardAgent under Host * to "yes" then it would be forwarded. As ForwardAgent under Match canonical was not being parsed I thought there was a bug and I made a report to OpenSSH (which was deleted).

It was explained to me that it was working as intended:

When hostname canonicalisation is enabled, the configuration is parsed twice. An initial pass to collect options and then a second pass after the hostnames are finalised. Most configuration options operate as "first match wins"

So what's happening here is that, on the first pass, your "Host *" block is being parsed and the ForwardAgent option is being set to "no". On the subsequent pass, the ForwardAgent directive in the "Match canonical" block is ignored because it's already set.

I can understand that. I knew it was parsed twice with CanonicalizeHostname enabled, but I didn't realise this consequence of that.

What I don't understand is the way to avoid that "by only setting the fallback ForwardAgent on the final pass":

Host bastion
    ProxyJump none

Match canonical
    ProxyJump bastion
    ForwardAgent yes

Match all
    CanonicalizeHostname always
    CanonicalDomains mydomain.co.uk
    CanonicalizeMaxDots 0
    CanonicalizeFallbackLocal yes

Match canonical all
    ForwardAgent no

My questions:

  • How is Match All different to Host *?

  • What does Match canonical all mean? Why would the canonical hosts be directed to parse that option again when it has already been set for them in a different way? If it was !canonical it would make more sense to me, although seemingly redundant, as in this case the single directive under it has already been set.

I have a lot of other options to add, which were mostly under Host * and have nothing to do with CanonicalizeHostname, so I had left them out, but now I don't know where to put them.

3
  • if i understood the question correctly you are wondering why something is happening because a directive you are using is being overwritten by another one although that's how ssh does it and now you are looking for an explanation or think this is a bug?
    – SyncToy
    Apr 3, 2023 at 21:38
  • @SyncToy The questions I am asking are at just above your comment.
    – paradroid
    Apr 3, 2023 at 21:40
  • 2
    There is more than one question
    – SyncToy
    Apr 3, 2023 at 21:58

1 Answer 1

3

You can create configuration blocks that are only applicable to a specific subset of hosts. The Match all block matches all hosts and is equivalent to Host *.

The Match Canonical All block applies to all hosts that have undergone canonicalization.

Use Match canonical all instead of Match !canonical to be sure that the configuration option is only applied to hosts that have undergone canonicalization and not too those hosts where explicit hostname has been specified.

Match all is default. If you want to apply options on all hosts, with or without canonicalization, then choose that option. If you want to apply it only for hosts that undergo canonicalization, then create additional Match blocks below that specify what is allowed by your options

The ssh client connects to a host and determines the first matching configuration file. If there are multiple matching files, the options in the first matching Host or Match block take precedence.

In order for the CanonicalizeHostname option to work properly, you must enable the double-pass parsing mode. During each pass of parsing, filters and Match blocks are evaluated for every hostname in your configuration file. The filters are evaluated first, followed by the Match blocks.

You can add options to the Match blocks based on their intended scope. If the option applies to all hosts, place it under Match all. If it applies only to hosts that have undergone canonicalization, create a new Match block with those options and apply them accordingly.

Sources:

Hostname canonicalisation in OpenSSH

Some notes on OpenSSH's optional hostname canonicalization

How to perform hostname canonicalization

Host Restricts the following declarations (up to the next Host or Match 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 usually the hostname argument given on the command line (see the CanonicalizeHostname keyword for exceptions). 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.

ssh_config(5) — Linux manual page

Examples:

# Match on hostname
Match Host example.com

# Match ip
Match Address 192.168.178.*

# Match on user
Match User alice

# Match based on group
Match Group admin

# Multiple criteria
Match Host example.com User alice

# Match multiple criteria, use this key for some hosts
Match canonical,Host host.domain.cocom,192.168.178.100
IdentityFile ~/.ssh/my_key

# Match multiple criteria, disable port forwarding for some hosts
Match canonical,Host *.domain.com,*.internal,192.168.178.*
ForwardAgent no
ForwardX11 no
CanonicalizeHostname yes
CanonicalDomains example.org example.com
CanonicalizeMaxDots 1
CanonicalizeFallbackLocal yes
CanonicalizePermittedCNAMEs *.redis.example.org:redis.example.com

How is Match All different to Host *?

Host * matches all hosts, thus the configuration directives that follow will be applied to all hosts. The hostname can appear anywhere in the pattern you use (either as part of the pattern itself, or at the end after an asterisk).

A "Match All" keyword is used to group all the configuration directives that apply to a particular host. When you use "Match All", you don't need to specify a pattern, as it will match all hosts by default.

Examples:

Host *
   <config directives>
Host *
   StrictHostKeyChecking no
Match All
   <config directives>
Match All
   Compression yes

What does Match canonical all mean? Why would the canonical hosts be directed to parse that option again when it has already been set for them in a different way? If it was !canonical it would make more sense to me, although seemingly redundant, as in this case the single directive under it has already been set.

Match canonical all is a configuration option that applies to all canonical hosts, which are hosts whose name can be fully qualified by the DNS domain name search algorithm. The "canonical" keyword in SSH means that the hostname must be fully qualified, as opposed to an alias or shortened version of the hostname.Regarding the second part of your question, if a configuration option is set for a host using a specific directive, and that host matches a "Match" block with the same option

"Match canonical all" applies to all canonical hosts, and if a host matches both a specific directive and a "Match" block with the same option, the "Match" block takes precedence. The "!" in "!canonical" means "not canonical". So, "Match !canonical" applies to non-canonical hosts that cannot be fully qualified by DNS domain name search algorithm

The canonical host matching option can be used on a per-host basis. A host matches if is listed in the DNS zone file, has an address configured using iproute2 and routes packets between interfaces of the same network device. If a host does not meet these criteria, then it doesn't match canonically

When the CanonicalizeHostname option is enabled in SSH, the hostname of the remote host is parsed twice: first, when the connection is established, and second, when the SSH client applies any Match blocks that have a CanonicalizeHostname directive. If you have a Match block with a CanonicalizeHostname directive that applies to a canonical host, the host's name will be parsed twice, and the configuration option in the Match block will take precedence over any specific directive that was set earlier.

3
  • "The Match Canonical All block applies to all hosts that have undergone canonicalization." - but why was I given the config above? This appears to be wrong. I have all hosts being affected by the directives under it unless they have already been set above. That statement seems to apply to Match canonical, without the all, but this is still confusing.
    – paradroid
    Apr 3, 2023 at 20:44
  • "The filters are evaluated first, followed by the Match blocks." - What are these filters?
    – paradroid
    Apr 3, 2023 at 20:48
  • "In order for the CanonicalizeHostname option to work properly, you must enable the double-pass parsing mode." - I can't see any reference in the man file about this and it;s working fine by default as far as I can tell.
    – paradroid
    Apr 3, 2023 at 20:51

You must log in to answer this question.

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