Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

ssh

I'm trying to use multiple deploy keys for github, which means I need to match my SSH configs based on the repository name. I cannot match based on hostname, port or user because they are all the same. Only repo name and deploy keys are different.

I know this question has been asked before but I can't find a satisfactory answer because I am not interested in adding a subdomain or user to my github ssh URL. This question is specifically on how to match a URL PATH or SSH argument or other workaround that do not involve modifying the default github ssh URL.

The default ssh github url is [email protected]:githubusername/githubreponame.git

The usual SSH configs are:

# repo 1
# MATCH or HOST xxxxxxxxxxx
    HostName github.com
    User git
    IdentityFile ~/.ssh/myrepo1name_rsa

# repo 2
# MATCH or HOST xxxxxxxxxxx
    HostName github.com
    User git
    IdentityFile ~/.ssh/myrepo2name_rsa

As per the ssh config manual http://man7.org/linux/man-pages/man5/ssh_config.5.html you can use 2 parameters to match a config, HOST or MATCH.

We already know that HOST cannot do it. So MATCH is the only way:

The available criteria keywords for MATCH are: canonical, final, exec, host, originalhost, user, and localuser.

From those MATCH criteria keywords, it seems none of them have access to the repo name or the SSH arguments or anything useful.

The MATCH exec criteria keyword seems promising:

The exec keyword executes the specified command under the user's shell. If the command returns a zero exit status then the condition is considered true.

exec has access to the following variables http://man7.org/linux/man-pages/man5/ssh_config.5.html#TOKENS:

%h    The remote hostname.
%i    The local user ID.
%L    The local hostname.
%l    The local hostname, including the domain name.
%n    The original remote hostname, as given on the command line.
%p    The remote port.
%r    The remote username.
%u    The local username.

None of these variables appear useful to me.

I have tried the following:

Match exec "pwd | grep myreponame1"
    HostName github.com
    User git
    IdentityFile ~/.ssh/myreponame1_rsa

It is partly successful, as usually when I am using git I am inside the repo directory so pwd | grep reponame will exit true. But it doesn't work for cloning or if I use a different directory name.

I am looking for a better alternative, which would probably also use MATCH exec in some way.

Edit: the best solution I found so far is:

Match exec "git remote get-url origin | grep reponame || echo $REPO | grep reponame"

But I'm not 100% happy with it because I need to add an env variable before git clone.

like image 923
cooldude101 Avatar asked Mar 14 '20 14:03

cooldude101


People also ask

How does git decide which SSH key to use?

Git does not know, or care. It just runs ssh. Specifies that ssh(1) should only use the authentication identity files configured in the ssh_config files, even if ssh-agent(1) or a PKCS11Provider offers more identities.

How do you use deploy keys on GitHub?

From your repository, click Settings. In the sidebar, click Deploy Keys, then click Add deploy key. Provide a title, paste in your public key. Select Allow write access if you want this key to have write access to the repository.

Can I use the same SSH key for multiple repositories?

As noted in "Why can't I use one ssh key on more than one github repo?" by tkeeler: This workflow becomes a problem when using automated systems and git submodules. You can't use the same deploy key in more than one repo, so the workaround becomes adding that key to their user account (or a dedicated machine account).


3 Answers

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.
like image 64
blaxpy Avatar answered Oct 22 '22 17:10

blaxpy


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.

like image 23
Camden Narzt Avatar answered Oct 22 '22 19:10

Camden Narzt


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
like image 8
chepner Avatar answered Oct 22 '22 17:10

chepner