26

Recently OpenSSH on macOS Sierra was upgraded to 7.3p1 which means that the Include config directive is available, hurray!

However I'm having problems actually using it.

I have the following ~/.ssh/config:

Host github.com
Hostname github.com
User git
IdentityFile ~/.ssh/keys/github_rsa

Host den
HostName narzt-desktop.local
User camden
GSSAPIAuthentication no
AddressFamily inet

Host walle
User CamdenNarzt
HostName WALLE.local
AddressFamily inet
GSSAPIAuthentication no

Include ~/.ssh/config.d/*

I saw here that there might be some useful debug output if I added a bunch of -v flags to my ssh commands, yet when I test the config for a host in one of the included files I get this:

$ \ssh -vvvvG git-codecommit.us-east-1.amazonaws.com
OpenSSH_7.3p1, LibreSSL 2.4.1
debug1: Reading configuration data /Users/camdennarzt/.ssh/config
debug3: /Users/camdennarzt/.ssh/config line 31: Including file /Users/camdennarzt/.ssh/config.d/family.conf depth 0 (parse only)
debug1: Reading configuration data /Users/camdennarzt/.ssh/config.d/family.conf
debug3: /Users/camdennarzt/.ssh/config line 31: Including file /Users/camdennarzt/.ssh/config.d/icloud.conf depth 0 (parse only)
debug1: Reading configuration data /Users/camdennarzt/.ssh/config.d/icloud.conf
debug3: /Users/camdennarzt/.ssh/config line 31: Including file /Users/camdennarzt/.ssh/config.d/metabolistics.conf depth 0 (parse only)
debug1: Reading configuration data /Users/camdennarzt/.ssh/config.d/metabolistics.conf
debug3: /Users/camdennarzt/.ssh/config line 31: Including file /Users/camdennarzt/.ssh/config.d/scanimetrics.conf depth 0 (parse only)
debug1: Reading configuration data /Users/camdennarzt/.ssh/config.d/scanimetrics.conf
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 20: Applying options for *
debug1: /etc/ssh/ssh_config line 56: Applying options for *
user camdennarzt
hostname git-codecommit.us-east-1.amazonaws.com
port 22
addressfamily any
batchmode no
canonicalizefallbacklocal yes
canonicalizehostname false
challengeresponseauthentication yes
checkhostip yes
compression no
controlmaster false
enablesshkeysign no
clearallforwardings no
exitonforwardfailure no
fingerprinthash SHA256
forwardagent no
forwardx11 no
forwardx11trusted no
gatewayports no
gssapiauthentication no
gssapidelegatecredentials no
hashknownhosts no
hostbasedauthentication no
identitiesonly no
kbdinteractiveauthentication yes
nohostauthenticationforlocalhost no
passwordauthentication yes
permitlocalcommand no
protocol 2
proxyusefdpass no
pubkeyauthentication yes
requesttty auto
rhostsrsaauthentication no
rsaauthentication yes
streamlocalbindunlink no
stricthostkeychecking ask
tcpkeepalive yes
tunnel false
useprivilegedport no
verifyhostkeydns false
visualhostkey no
updatehostkeys false
canonicalizemaxdots 1
compressionlevel 6
connectionattempts 1
forwardx11timeout 1200
numberofpasswordprompts 3
serveralivecountmax 3
serveraliveinterval 0
ciphers [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected],aes128-cbc,aes192-cbc,aes256-cbc,3des-cbc
hostkeyalgorithms [email protected],[email protected],[email protected],[email protected],[email protected],ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
hostbasedkeytypes [email protected],[email protected],[email protected],[email protected],[email protected],ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
kexalgorithms [email protected],ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1
loglevel DEBUG3
macs [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1
pubkeyacceptedkeytypes [email protected],[email protected],[email protected],[email protected],[email protected],ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
xauthlocation /opt/X11/bin/xauth
identityfile ~/.ssh/id_rsa
identityfile ~/.ssh/id_dsa
identityfile ~/.ssh/id_ecdsa
identityfile ~/.ssh/id_ed25519
canonicaldomains
globalknownhostsfile /etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2
userknownhostsfile ~/.ssh/known_hosts ~/.ssh/known_hosts2
sendenv LANG
sendenv LC_*
connecttimeout none
tunneldevice any:any
controlpersist no
escapechar ~
ipqos lowdelay throughput
rekeylimit 0 0
streamlocalbindmask 0177

(Ignore the line numbers in the first bit, I deleted some comments to save space in the question) The user should be the user specified in the ~/.ssh/config.d/metabolistics.conf file, and the identity file should likewise be the one specified in the included config file. I can't post the contents of the ~/.ssh/config.d/metabolistics.conf file, but it's format is exactly the same as the main ~/.ssh/config file but without any further includes.

I checked the permissions and they look fine to me:

$ ls -lhRa ~/.ssh/config* 
-rw-------  1 camdennarzt  staff   541B  1 Jan 14:22 /Users/camdennarzt/.ssh/config

/Users/camdennarzt/.ssh/config.d:
total 32
drwxr-xr-x  6 camdennarzt  staff   204B  1 Jan 14:37 .
drwx------  9 camdennarzt  staff   306B  1 Jan 14:22 ..
-rw-------  1 camdennarzt  staff   260B  1 Jan 14:16 family.conf
-rw-------  1 camdennarzt  staff   303B  1 Jan 14:17 icloud.conf
-rw-------  1 camdennarzt  staff   524B  1 Jan 14:15 metabolistics.conf
-rw-------  1 camdennarzt  staff   1.6K  1 Jan 14:15 scanimetrics.conf
Camden Narzt
  • 1,099
  • 8
  • 18

3 Answers3

46

Figured it out myself. The clue was in the ssh_config man page:

Include

Include the specified configuration file(s). Multiple pathnames may be specified and each pathname may contain glob(3) wildcards and, for user configurations, shell-like ``~'' references to user home directories.

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.

I had my Include statement trailing a Host directive so it was being included into that Host's config.

Jesper Rønn-Jensen
  • 6,910
  • 7
  • 27
  • 34
Camden Narzt
  • 1,099
  • 8
  • 18
  • 16
    In other words the `Include` directive must go to the top of the `config` file (before the "body" made of `Host` blocks) – lucianf Aug 06 '18 at 12:13
  • 2
    Thanks, while that does follow from the docs it isn't immediately obvious when trying to figure out why a config isn't working :) – larsks Oct 15 '18 at 02:09
  • @larsks Agree, wasted so many hours debugging because of this rule. – mss Oct 14 '21 at 20:43
  • How does _"Include directive may appear inside a Match or Host block to perform conditional inclusion."_ imply _"Include must go at the top of the file in order to wortk"_? – Alecz Dec 15 '21 at 14:54
  • Indenting the `Host`/`Match` blocks with a TAB _really_ helps debugging, and also remembering that _the only global statements are the start of a file, everything after the first block belongs to a block_ (that's why you need `Match all` or `Host *` for trailing statements) – MestreLion Jun 03 '23 at 23:28
14

You can leave the Include statement at the end of the file if you precede it with Match all. This terminates the previous Host/Match, and then conditionally always includes the file(s). So, the file would end with:

Match all
Include config.d/*
Iiridayn
  • 398
  • 1
  • 5
  • 18
  • As an aside, only lines starting with `#` or empty lines count as comments; you can't put a comment on the same line as `Match` or `Host`. – Iiridayn Apr 17 '20 at 00:18
4

actually no. It's a short-circuit bug in the SSHCONF_NEVERMATCH flag's use. I'm working on diffs to fix the mess. Includes should be able to go anywhere (and also be recursive) be it main body or inside a Host|Match block. The only tricky bit is knowing when you've unrolled the stack of read_config_file_depth() and can resume processing Host|Match again.

I'll be posting to my branch when I have something.

MattP
  • 41
  • 1