About Git and GitHub permission(SSH and HTTPS, single account and multiple accounts)

It is Zion
11 min readJun 27, 2020

When the local computer communicates with GitHub, the transmission is mainly based on two protocols, HTTPS and SSH, and the corresponding repository addresses are HTTPS URLs and SSH URLs.

The first thing to emphasize is that HTTPS URLs and SSH URLs correspond to two completely independent permission verification methods. The main difference is that HTTPS URLs use account passwords for verification, and SSH URLs use SSH key pairs for verification. In normal use, we can choose one according to the actual situation.

HTTPS URLs

GitHub officially recommends the use of HTTPS URLs, because this method is more applicable (even in the case of a firewall or proxy), and is more convenient to use (easier configuration).

When using the HTTPS URLs address clone /fetch/pull/push repository, there is no need to configure the local system in advance, just enter the GitHub account and password. However, if you have to manually enter the account password every time, it is also a very tedious thing.

Fortunately, there are already multiple mechanisms to make operations less troublesome.

In the macOS, when the Keychain mechanism is enabled, after entering the GitHub account password for the first time, the authentication information will be automatically saved in the Keychain.app of the system, and the authentication information saved in the Keychain.app will be automatically read the next time the repository is accessed again.

On other systems, although there is no Keychain mechanism, Git provides a credential helper mechanism, which can cache the account password in memory for a period of time (default 15 minutes), or store it as a file (such as ~/. git-credentials). Of course, if the macOS does not enable the Keychain mechanism, this method can also be used.

# cache credential in memory
$ git config --global credential.helper cache
# store credential in ~/.git-credential
$ git config --global credential.helper store

When credential.helper is set to store, after entering the GitHub account password for the first time, it will be automatically saved to the such as ~/.git-credentials file in the form of https://user:pass@github.com ; next time When accessing the repository, the authentication information saved in ~/.git-credentials will be automatically read.

Or you can use the token mechanism. In the Developer Settings of the Github user’s Settings, you can create and manage tokens in the Personal access tokens column, as long as the token id is saved locally. Then in a repository, in the config file, just change the format of the remote url part to https://your_token_id@github.com/your_repo_name.git (note: later, when more remote repo interactions such as git clone, the URL must bring the token id) .

Another situation that needs to be explained is that if 2FA (two-factor authentication) is enabled in GitHub, when entering the GitHub account password in the local system, you cannot enter the original password (that is, the login password of the GitHub website), but need to Create a Personal access token in the GitHub website in advance, and then use the access token as a password to enter when accessing the code repository needs to be verified.

SSH URLs

In addition to HTTPS URLs, you can also use SSH URLs to access the GitHub code repository.

Before using SSH URLs, you need to generate an SSH key pair (secret key pair, including private and public keys) on the local computer. By default, the generated secret key is located in the such as $HOME/.ssh/ directory, and the file are id_ed25519 and id_ed25519.pub (if you choose ed25519 algorithm), respectively. Usually, you do not need to modify them and keep the default. However, if there are multiple key pairs in a computer, you need to modify the key file name. There is no mandatory naming convention for the name, so you can easily identify it.

$ ssh-keygen -t ed25519
Generating public/private ed25519 key pair.
Enter file in which to save the key (/Users/xxx/.ssh/hehe_ed25519): /Users/xxx/.ssh/hehe_ed25519 # your files name # if enter directly, name becomes id_ed25519
Enter passphrase (empty for no passphrase): <myPassphrase> # if enter directly, passphrase becomes empty
Enter same passphrase again: <myPassphrase>
Your identification has been saved in /Users/xxx/.ssh/hehe_ed25519.
Your public key has been saved in /Users/xxx/.ssh/hehe_ed25519.pub.
The key fingerprint is:
SHA256:jCyEEKjlCU1klROnuBg+UH08GJ1u252rQMADdD9kYMo
mail@outlook.com # email is no use, you can remove this
The key's randomart image is:
+--[ED25519 256]--+
| ... |
|.... . o |
|o.. . . o = o |
|o.. . o.O + |
|.o SoOo+ |
|. . o=.ooo |
| o o + .o..+. |
|. . . o.o +o+ . |
| .E.+*oo++ |
+----[SHA256]-----+

In the process of creating the key, the system also recommends creating a thing called passphrase, what is it for?

First of all, using passwords alone is certainly not secure enough. If the password is too simple, it is easy to be brute-forced. If the password is too complicated, it is difficult for the user to remember, and it is even more insecure to record it in a small book.

Therefore, SSH keys were born. The reliability of the SSH key pair is very high, and there is basically no possibility of brute force cracking. However, this requires the user to keep the private key very carefully. If someone secretly copies your private key when using your computer, then it is like someone else has got your home key and can open your home door at any time.

Based on the above situation, the solution is to add a password in addition to SSH keys, that is, passphrase. Only when both SSH private key and passphrase are available, can the SSH permission check be passed, which greatly increases the security. Of course, this passphrase is not necessary, and you can not set the passphrase when creating the key pair.

In addition, if you need to enter a passphrase every time the authority is verified, this is quite troublesome. Fortunately, we don’t have to worry about this problem anymore, because ssh-agent can help us remember passphrase, macOS Keychain can also remember passphrase, so we don’t need to re-enter the password on the same computer.

After the key pair is created, the private key is stored on the local computer (~/.ssh/hehe_ed25519), and the content of the public key (~/.ssh/hehe_ed25519.pub) is added to the GitHub account.

However, if the connection status between the local computer and GitHub is detected at this time, the system will still prompt that the permission verification fails.

$ ssh -T git@github.com
Permission denied (publickey).

This is because when the local computer establishes a connection with GitHub, the ssh-agent of the local computer actually communicates with the GitHub server. Although the local computer has a private key, ssh-agent does not know where the private key is stored. Therefore, to use the key pair normally, you need to add the private key to the local computer’s ssh-agent (passphrase is required during the addition process).

# start ssh-agent in the background
$ eval "$(ssh-agent -s)"
Agent pid 78370

$ ssh-add ~/.ssh/hehe_ed25519
Enter passphrase for /Users/xxx/.ssh/hehe_ed25519: <myPassphrase>
Identity added: /Users/xxx/.ssh/hehe_ed25519 (/Users/xxx/.ssh/hehe_ed25519)

After the addition is complete, you can view the key stored in the current computer.

$ ssh-add -l                
4096 SHA256:xRg49AgTxxxxxxxx8q2SPPOfxxxxxxxxRlBY /Users/xxx/.ssh/hehe_ed2519 (ED25519)

Check the connection status of the local computer and GitHub again, and the verification passes normally.

$ ssh -T git@github.com
Hi xxx! You've successfully authenticated, but GitHub does not provide shell access.

When you perform the clone /fetch/pull/push operation later, you can access the GitHub code repository normally, and you do not need to re-enter the account password.

Moreover, after adding the private key to the ssh-agent, even if the private key file is deleted, the local computer can still normally access the GitHub code repository.

$ rm -rf ~/.ssh$ ssh-add -l    
4096 SHA256:xRg49AgTxxxxxxxx8q2SPPOfxxxxxxxxRlBY /Users/xxx/.ssh/id_rsa (ED25519)
$ ssh -T git@github.com
The authenticity of host 'github.com (192.30.252.130)' can't be established.
RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,192.30.252.130' (ED25519) to the list of known hosts.
Hi xxx! You've successfully authenticated, but GitHub does not provide shell access.

Only after executing the ssh-add -D or ssh-add -d pub_key command to delete the private key from the ssh-agent will the authentication information become invalid.

$ ssh-add -d ~/.ssh/hehe_ed25519.pub
Identity removed: /Users/xxx/.ssh/hehe_ed25519.pub (mail@outlook.com)
$ ssh-add -l
The agent has no identities.
$ ssh -T git@github.com
Permission denied (publickey).

Use multiple GitHub accounts simultaneously on one machine

After familiar with the two verification methods of HTTPS URLs and SSH URLs, let’s look at the problems encountered before. What do I need to do to use multiple GitHub accounts to access different repositories on the same computer?

For better demonstration, it is assumed that there are two GitHub accounts, one for work and one for own use, called worker and myself, and there is a repository in each of the two accounts, worker/ss and myself/tt.

As mentioned earlier, HTTPS URLs and SSH URLs correspond to two independent sets of permission verification methods, so both sets of methods should be able to achieve our needs independently.

But before explaining the problem of Git permission verification in detail, let’s review the priority of Git configuration file first.

  • /etc/gitconfig: stores the git configuration information of all users of the current system. When using git config with the — system option, the configuration information will be written to this file;
  • ~/.gitconfig or ~/.config/git/config: store the git configuration information of the current user. When using git config with the — global option, the configuration information will be written to this file;
  • Keychain Access: When the Keychain mechanism is enabled, the account password will be automatically saved to Keychain Access after permission verification;
  • The config file in the Git directory of the repository (that is, repo/.git/config): stores the git configuration information of the current repository. When using git config with the — local option in the repository, the configuration information will be written to this file.

In terms of priority, the priority of the above 4 configuration items rises from top to bottom, that is, repo/.git/config has the highest priority, then Keychain Access will overwrite the configuration in ~/.gitconfig, and ~/.gitconfig will Overwrite the configuration in /etc/gitconfig.

Multi-account coexistence based on SSH protocol

First look at how to use SSH URLs to achieve our needs.

# ssh-add -l
4096 SHA256:lqujbjkWM1xxxxxxxxxxG6ERK6DNYj9tXExxxxxx8ew /Users/xxx/.ssh/worker_ed25519 (ED25519)
4096 SHA256:II2O9vZutdQr8xxxxxxxxxxD7EYvxxxxxxbynx2hHtg /Users/xxx/.ssh/myslef_ed25519 (ED25519)

Before explaining the problem of coexistence of multiple accounts in detail, let’s think back and forth about the scenario of interacting with the GitHub repository in terminal.

$ git add .
$ git commit -m "add README"
$ git push forked HEAD
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 310 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com: worker/ss.git
7df6839..68d085b nightly -> nightly

During the operation, the local computer’s ssh-agent established a connection with the GitHub server and checked account permissions.

When the local computer has only one GitHub account, this behavior is not difficult to understand, and the system should use this unique GitHub account for operation. So if there are multiple Github accounts on the local computer, what is the system to determine which account should be selected?

The actual situation is that the system cannot judge. The system will only have a default account, and then use this default account to operate all code repository. When the account and the repository do not match, it will report an error of permission verification failure.

How can the system distinguish the accounts correctly? This requires us to manually configure, the configuration file is ~/.ssh/config.

Create ~/.ssh/config file, fill in the following content.

Host Worker
AddKeysToAgent Yes
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/Worker_ed25519
Host Myself
AddKeysToAgent Yes
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/Myself_ed25519

To understand the meaning of the above configuration file is not difficult, we can compare the SSH URLs of the two projects:

git@github.com:worker/ss.git
git@github.com:myself/tt.git

Among them, git is the username (ie User) used by the local ssh-agent to establish an SSH connection with the GitHub server, and http://github.com is the host (ie. HostName) of the GitHub server.

It can be seen that if the original SSH URLs are used, because the User and HostName are the same, the local computer does not know which SSH-key should be used to establish the connection.

Therefore, by creating ~/.ssh/config file to distinguish in Host, and then mapping to HostName through NAME, and then pointing to different SSH-key, namely IdentityFile. Because HostName is the field that really specifies the host of the GitHub server, this configuration will not affect the local ssh-agent connection to the GitHub host, plus the Host alias points to a different SSH-key, thus achieving the two GitHub accounts Separate.

After the configuration is complete, the two GitHub accounts can be distinguished by the Host alias. In subsequent communication with the GitHub server, the host alias can be used instead of the original http://github.com. For example, to test the connectivity between the local ssh-agent and the GitHub server, you can use the following methods:

$ sh -T git@Worker
Hi Worker! You have successfully authenticated, but GitHub does not provide shell access.
$ ssh -T git@Myself
Hi Myself! You have successfully authenticated, but GitHub does not provide shell access.

It can be seen that at this time, the two accounts are responsible for their duties, and there will be no confusion.

However, we have missed a very important point. When performing push/pull/fetch and other operations in the local code repository, the command does not contain Host information. How does the system know which GitHub account we want to use for operation?

The answer is that the system is still unable to judge, and we need to specify the configuration.

Obviously, different repositories may correspond to different GitHub accounts, so this configuration cannot be configured globally, but can only be configured separately in each project, that is, the repo/.git/config file.

The configuration method is as follows:

In the worker/ss repository:

$ git remote add forked git@worker:worker/ss.git

In the myself/tt repository:

$ git remote add forked git@myself:myself/tt.git

The principle of configuration is also easy to understand, which is to replace the host of the repository with the alias set previously. After the addition is complete, when performing any git operations in the two repositories, the system can select the correct SSH-key to interact with the GitHub server.

Multi-account coexistence based on HTTPS protocol

With the previous experience, our thinking is much clearer. After verification of Git permissions using HTTPS URLs, the system will store the GitHub account password in the Keychain, or in the ~/.git-credentials file (Git credential helper).

No matter where it is stored, the problem we face is the same, that is, how to distinguish which GitHub account is used in the code repository.

The configuration method is actually very simple:

In the worker/ss repository:

$ git remote add origin https://worker@github.com/worker/ss.git

The principle of configuration is also easy to understand. Add the GitHub user name to the Git address of the repository, so that when the git command is executed, the system will use the specified GitHub user name to find the corresponding keychain or ~/.git-credentials With authentication information, the problem of messy account usage is gone.

For tokens, just add the token id to the URL, and the system can naturally distinguish users based on the id.

Finally, it is very important that git account corresponds to GitHub account. If you change your global git name and email to a non-existent name and email locally, after git push you will find that the commit author displayed on GitHub is a ghost account.

Therefore, the name and email of git should be the same as the GitHub account. For different GitHub accounts, it is best to configure the name and email of git in each repo/.git/config.

--

--