0

So I work from home and have my machine set up with my personal git credentials. I have been sharing access to my work projects with my personal git, but now that working from home has officially become permanent, I felt I should fix this issue and have all my commits on work projects come in through my work git account.

So after looking around, I found I could just change the local repos individually to use my work git credentials. Easy enough.

So I ran the commands:

git config --local user.name workUserName

git config --local user.email work@email.com

I then ran...

git config --local --get user.name

... to double check it had worked, and it did. I made my pushes, and the repos started successfully labeling the commits as coming through my work account.

So I started a new project today that wasn't shared with my personal git account, and only shared with my work account. I ran the same git commands to swap the local credentials and check that the changes were successful, and everything checked out. When I try to make a push though, it says I don't have access to the repository.

remote: Permission to repository.git denied to personalGitAccount.

The only difference seems to be that this repo isn't shared with my personal git account, which are the global credentials on my machine. I can't understand why that would have any relevance on a repository that has had the local credentials changed. Do I need to have this repo shared to both my personal and my work accounts to make any pushes to the codebase?

Dale Spiteri
  • 767
  • 1
  • 9
  • 21
  • 4
    `user.name` and `user.email` configuration values are _not_ credentials. Those are just the values used to fill in the author when creating a commit. – knittl Jul 22 '21 at 19:26

1 Answers1

3

As knittl noted in a comment, user.name and user.email aren't credentials and are not used during credential setup and checking. That leaves a big question, though: Where do any credentials come from? Unfortunately, the answer is: it depends, which means we have another question: depends on what? The first part of that is the URL you're using:

  • An http or https URL causes Git to go through libcurl, which means you get libcurl authentication and credentials. This is then complicated by your OS: libcurl is built with OS-specific code.
  • An ssh URL causes Git to go through ssh, which means you get ssh authentication and credentials. This is then complicated by your OS: ssh is not only built with OS-specific code, but on Windows, the Git distributions tend to include an ssh implementation in case Windows doesn't have one, and yet some versions of Windows have one.

This gets you to the next steps to take. That's as far as we can actually go, given the information you provided—but I have a guess based on the fact that you've included the tag and that GitHub have been pushing people to use ssh (by, among other things, temporarily disabling https access entirely now and then). I don't know what your OS is, so I can't include Windows- or macOS-specific items (and wouldn't anyway as I don't use Windows and try not to depend on macOS-specific tricks since I use Unix/Linux-y systems a lot too). But we can get pretty far just assuming ssh-and-GitHub, because of the way GitHub has you "log in" to their computers.

SSH authentication

When using ssh in general, the way authentication works is a multi-step process:

  • First, your computer looks up any host name you provide (via ssh user@host command or scp file user@host:path or whatever; with Git URLs it is ssh://user@host/path or user@host:path if you use the shorthand syntax). This gets translated to an IP address—IPv4 or IPv6—to use to contact the host. Or, if you provide an IP address directly, ssh uses that.

  • Next, your ssh contacts the ssh server at that host and obtains the server's "fingerprint". Your computer can compare this fingerprint to one stored in a known_hosts file or similar. Your ssh may, depending on options, now ask you to confirm that this is the machine you intended to contact, or if it's already listed in the known_hosts file and the fingerprint matches, just assume that it's the right machine.

  • Your ssh client and the ssh server (sshd) now go through a fairly fancy dance where they exchange protocol information and key-exchange information. This is so that they can encrypt stuff even before they get to the point of doing authentication. We get to ignore all of this here, although there's a lot to it. (Use the -v option to ssh, multiple times for more debug information, and you'll see some of this happening.)

  • Eventually, your Git-invoked ssh provides, to the server, the user name you provided—which, for GitHub, must be git—along with a public key. Since the user name that Git must provide here must be git, we get to ignore this one too! (Except that we have to spell it out somewhere, e.g., in the URL as ssh://git@github.com/path/to/repo.git.) The server—in this case, GitHub—uses this information to figure out who you are claiming to be. We'll come back to this in just a moment.

  • Now that the server sshd knows you you claim to be, sshd sends you more stuff which your ssh must decrypt using your private key. If your ssh is able to do this, the server sshd now believes that you are indeed the person you claimed to be. Data may now flow across the ssh/sshd connection; on GitHub, sshd now invokes the GitHub Git (through some extra code that the GitHub folks wrote, which has access to the person-you-claimed-to-be information as verified by GitHub's sshd: this extra code does whatever checking is appropriate).

The boldface sentence above therefore contains the key (if you'll pardon the expression) to making the GitHub sshd authenticate you "correctly", for your personal definition of "correct". Since you must claim to be git, the actual user name is not provided here at all. Instead, it's secretly smuggled in via the public key.

When you use the GitHub web interface to set up your public-key access to your repositories, you authenticate to GitHub over https (not ssh). This gives you a place to type in a user name, along with a place to copy-paste an ssh-keygen-generated public key. When you do all of this, GitHub saves the public key along with your user name. Later, when you give GitHub a public key, they look up that public key in their database of "all public keys that have some user using them". This database provides them the user name.

What this means, bottom line, is: When using ssh on GitHub, the user name you're using to authenticate doesn't come over the Internet at all. It's just assumed via the public key you send. You must therefore be very precise about which public key(s) you send. The first public key that you send and that they accept determines who they believe you are.

Your ssh will, in general, try keys from your "key ring" one at a time until one works, or it runs out of keys. This creates problems.

You have one user name per public key for GitHub

If you have only one public key that you use with GitHub, everything is easy(ish). There's only one key that can work in the lock. That one key is associated with your one user name on GitHub. You use that key and they know who you are.

If you have two public keys, though, and two user names, now it gets complicated. Each of your two public keys has one user name attached. Let's call these keys A and B, and the associated user names ua and ub.

  • If you present key A first, it works and GitHub believe you are ua.

  • If you present key B first, it works and GitHub believe you are ub.

If ua and ub have access to different repositories, you're going to have to be careful which key you present.

(There's a related problem: if you have thousands or millions of keys, and you let your ssh try them all one at a time, including all the wrong ones, their sshd may grow suspicious. Imagine how you'd feel if you were at home and some stranger came up to your locked front door and just started trying a million keys in your lock. We'll ignore this here, though.)

Controlling the key your ssh presents

Each OS can have its own add-ons for ssh, and they may throw extra wrinkles into this process, but at the base ssh-for-all-OSes level, ssh itself will read a configuration file. In this configuration file, you get to set various options:

  • IdentityFile names a file on your system that contains the public and/or private keys (they're normally paired, so that ~/.ssh/id1 and ~/.ssh/id1.pub are the private and public keys for id1, for instance).
  • The IdentitiesOnly flag tells your ssh not to offer additional keys not listed via IdentityFile lines.
  • User is convenient: you can use this to avoid having to type git@.
  • HostName is convenient as well: see below.

You will therefore want to use at least these options: IdentitiesOnly to tell your ssh don't try other keys and IdentityFile to tell your ssh do try this key.

To get your ssh program to match the right entry, you will probably want to use Host lines. Here's an example ~/.ssh/config file. Be careful about cut and pasting from this; see bold text below.

Host gh-work
        HostName github.com
        User git
        IdentityFile ~/.ssh/id_work.github.pub
        IdentitiesOnly yes
Host gh-home
        HostName github.com
        User git
        IdentityFile ~/.ssh/id_home.github.pub
        IdentitiesOnly yes

You can now use ssh://gh-work/corp/work-repo.git and ssh://gh-home/me/personal-project.git as the Git URLs for some corporate work repository and some personal project. The gh-work "host name" will match the appropriate entry in ~/.ssh/config. The User git and HostName github.com parts will replace gh-work with git@github.com, and the identity lines will make sure you present just the one correct key, so that GitHub identify you as your "work persona". The gh-home entry works nearly identically, but presents a different key, so that GitHub identify you as your "home persona".

These two entries list .pub files. This is the key you want your Git to send. This generally works well when you use an ssh-agent. It may or may not work if you don't use the agent. You may have to list the file names without the .pub part, so that your IdentityFile line points to the private key. Don't worry if you have to do this: your ssh will send over the public key, not the private one (use ssh -Tvv gh-work or ssh -Tvv gh-home to verify this). But do consider using the agent, so that you don't have to do this, nor even store the private key on that particular host at all in some cases. The agent adds an extra layer of security.

(There is much more you can do with ssh, but this is enough for now.)

torek
  • 448,244
  • 59
  • 642
  • 775
  • This is an exceptional answer. I had no idea the can of worms I was opening with this one and how complex the behind the scenes process was involving running two separate GitHub accounts. This definitely gives me a lot of Google ammo though with a better understanding of what the actual hurdles are I need to overcome. Thanks for the thorough reply. This should be a blog post. – Dale Spiteri Jul 23 '21 at 02:06