How can I match a CIDR range for an SSH config host entry?

Solution 1:

Matching against patterns in the ssh_config file is done as basic pattern matching, not as a network/CIDR matching. So using CIDR notation is not going to work.

The man page explains that:

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).

The best you can do is to use a list of more than one pattern. Again, from the manual page:

A pattern-list is a comma-separated list of patterns. Patterns within pattern-lists may be negated by preceding them with an exclamation mark (!).

So to cover your two /18 nets, you'd need to list:

  • all hosts matching 10.2.?.* (i.e. 10.2.0.0–10.2.9.255)
  • all hosts matching 10.2.??.* (i.e. 10.2.10.0–10.2.99.255)
  • all hosts matching 10.2.10?.* (i.e. 10.2.100.0–10.2.109.255)
  • all hosts matching 10.2.11?.* (i.e. 10.2.110.0–10.2.119.255)
  • all hosts matching 10.2.12?.* EXCEPT the ones matching 10.2.128.* and 10.12.129.* (and remember that the exclusion must come first!)

Your pattern list should then look like this:

Host "10.2.?.*","10.2.??.*", "10.2.10?.*","10.2.11?.*","!10.2.128.*","!10.2.129.*","10.2.12?.*"

Solution 2:

You could use the very powerful Match directive with the grepcidr tool:

Match exec "grepcidr 10.2.0.0/18 <(echo %h) &>/dev/null"
   Proxycommand ssh -q bastion-foo -W %h:%p

How this works:

  • %h in the commandline gets expanded to the hostname (see Tokens)
  • <(echo %h) wraps this into a temporary "file"
  • Maybe use <(dig +short %h) if working with hostnames
  • then use grepcidr to match the hostname %h against a.b.c.d/e
  • &>/dev/null because we only care about the return code, not the output.

This needs the additional tiny grepcidr package, which should exist in your package manager.