How to match SSH config based on URL path or SSH arguments (for github deploy keys)?

Solution 1:

You can specify an SSH key for a particular GitHub user or a repository.

Change a hostname with url rule in the global git config. Then select the correct identity for the hostname with Host rule in the SSH agent config.

Example:

GitHub username: user1.

Repository: [email protected]:user1/repo.git.

User identity file: ~/.ssh/id_rsa_user1.

~/.gitconfig or ~/.config/git/config:

[url "[email protected]:"]
    insteadOf = https://github.com/
[url "git@github-user1:user1"]
    insteadOf = [email protected]:user1

~/.ssh/config:

AddKeysToAgent  yes
IdentitiesOnly yes

Host github-user1
    HostName github.com
    IdentityFile ~/.ssh/id_rsa_user1

Host *.example.com
    IdentityFile ~/.ssh/id_rsa_example

Git will substitute [email protected]:user1 with git@github-user1:user1. So the new hostname will be github-user1. Then the SSH agent will use the new hostname to select the user1 identity file ~/.ssh/id_rsa_user1.

Now you can clone the repository with the following command:

git clone [email protected]:user1/repo.git

Notes:

Only one url rule is applied. So this will not work:

git clone https://github.com/user1/repo.git

SSH config manual:

man ssh_config

Parameters meanings:

  • AddKeysToAgent - store a key and its passphrase in the agent on first use.
  • IdentitiesOnly - only use configured identities even if the agent has more identities.

Default identity is ~/.ssh/id_rsa. If you use it do not set it via top level IdentityFile or Host * rule or you will experience the following problem:

  • The default identity is set with IdentityFile ~/.ssh/id_rsa.
  • The default identity is in the agent and id_rsa_user1 is not.
  • The agent will use the default identity for github-user1 host, despite Host github-user1 rule.

Solution 2:

The simplest thing to do is to create separate alias for each respository.

Host repo1
  HostName github.com
  User git
  IdentityFile ~/.ssh/myrepo1name_rsa

Host repo2
  HostName github.com
  User git
  IdentityFile ~/.ssh/myrepo2name_rsa

Then use repo1:githubusername/githubreponame.git and repo2:githubusername/githubreponame.git as the URLS in Git.

To make it less repetitive, you can define aliases with a pattern you can match on.

Host gitrepo1
  IdentityFile ~/.ssh/myrepo1name_rsa

Host gitrepo2
  IdentityFile ~/.ssh/myrepo2name_rsa

Host git*
  HostName github.com
  User git

Solution 3:

Here's pretty much what I do:

Host github.com gist.github.com
  Hostname %h
  User git
  RequestTTY no
  RemoteCommand none
  IdentitiesOnly yes
  ControlMaster no

Match host github.com,gist.github.com exec "~/Developer/C++/getargv/getargv -0 -s 1 $PPID | env POSIXLY_CORRECT=1 xargs -0 getopt '46AaCfGgKkMNnqsTtVvXxYyB:b:c:D:E:e:F:I:i:J:L:l:m:O:o:p:Q:R:S:W:w:' | perl -pe 's|.*? -- ||' | fgrep -e username1"
  IdentityFile ~/.ssh/keys/github_rsa

Match host github.com,gist.github.com exec "~/Developer/C++/getargv/getargv -0 -s 1 $PPID | env POSIXLY_CORRECT=1 xargs -0 getopt '46AaCfGgKkMNnqsTtVvXxYyB:b:c:D:E:e:F:I:i:J:L:l:m:O:o:p:Q:R:S:W:w:' | perl -pe 's|.*? -- ||' | fgrep -e username2"
  IdentityFile ~/.ssh/keys/github_rsa2

I set the common github configs in the top Host block, then each Match block looks at the arguments passed to ssh, parses them, and checks for the github username and if it matches, sets the correct key.

The ~/Developer/C++/getargv/getargv program is just an implementation of cat /proc/$PPID/cmdline for macOS, on linux you don't need a separate tool.

This works for cloning, pushing, pulling, etc.