I am really confused with the behavior of my bitbucket:
I have one project called
Prototype
, under which there are multiple repositories;I am able to push new commits to a repo named
Tiger
; And I assumed that I have had my laptop's ssh key set up in bitbucket's "Personal Settings/SSH Keys", because I would not be able to push commits toTiger
if I have not done so.There is another repo named
Cat
, and I just discovered that I am not able to push new commits to it. This really confused me since I am on the same laptop;So I checked my bitbucket's "Personal Settings/SSH Keys", and BOOM! There is no ssh key of my laptop stored in there!
So how am I able to push to Tiger
in the first place??? I could be mistaken... but I Do remember that I have had my laptop's ssh key stored in bitbucket.
- I then went ahead and tried to save my laptop's ssh key to bitbucket, but it is saying:
Someone has already added that SSH key.
What the heck is going on?
Any ideas?
CodePudding user response:
TL;DR
Fredrik's comment has the key insight (if you'll pardon the phrasing here):
you're trying to add the same key to two different bitbucket accounts
This is only a problem for ssh authentication, but it's a big problem for ssh authentication. You must use a unique public key per Bitbucket account.
Long: what's going on
Git itself contains no authentication. To do its distributed thing, your Git software must connect to some other Git software. The two software versions need not be the same—in fact, some servers may not use (C) Git at all—as long as they both speak the Git protocol, but the protocol has no authentication. It just assumes that by the time the protocol is in use, the server and client already trust each other completely. So before the hosting system believes that you are who you claim to be, you must somehow prove that you are. This is the authentication process.
In general, authentication consists of establishing some sort of user identity. Every web hosting site is a little bit different (for $reasons, sometimes having to do with actual dollars and sometimes $reasons
just being a metavariable) but there are two general and common ways to authenticate to any of the big Git hosting sites:
- http(s) (mostly https-only these days since plain http is terribly insecure), or
- ssh.
The https authentication has the most variability as there are many https implementations and each one has its own identity and authorization techniques, so it is the one about which we can say the least. However, both Bitbucket and GitHub now use token-based authentication rather than password-based. The difference between a token and a password is, essentially, that the token is a structured password, with the (hidden-from-the-user) structure containing extra information. One has the web site generate the token (using, usually, a password, and perhaps some second factor for two-factor authentication). One then directs one's Git to supply a user name and the token-as-the-password. This pair authenticates you as you to the hosting site. The user name here is your identity, and the password-or-token is your proof of identity (so guard it carefully!).
To use this method with Git, use a URL that begins with https://
. These take the form https://user@host/path/passed/to/host
(although you can shove the password / token in here, or leave out the user
part, it's usually simplest to include the user and leave out the password, so that only the password has to be managed via some credential system).
The ssh authentication is much more uniform, perhaps because there are fewer ssh implementations and most trace their history back to a common starting point. To use ssh authentication with Git, use a URL that begins with ssh://
or that has the form user@host:path
, e.g., [email protected]:my/repo.git
. The ssh system uses public-key cryptography; there is no password involved here, at least at this point.
Your Git software does not do any of the authentication on its own, but it can use a credential helper. It does so only for https authentication. This is highly configurable, and has a lot of system dependencies. It's also not what the original question is about, so that's all we'll say about it here.
Using ssh authentication with Git and big hosting sites
When you use ssh to authenticate, there's a user name involved, and it's right there in the URL: ssh://user@host/path
or user@host:path
. The ssh://
part (if using this form) and the at-sign `@ and colon (if using this form) are literal characters that will appear in the URL.
All the big Git servers require that you supply the user
part as the literal string git
. That is, they are going to authenticate you not as yourself but rather as the git
pseudo-user. There are a bunch of technical reasons for this, but they all boil down to "it was easier that way, and now it's the convention". Since you're going to claim to be this git
pseudo-user, not whoever you really are, the host is going to need to figure out who you really are in some other way. The way the big Git servers do this is by cheating.
If we look at public-key cryptography, we see that it works by having you (or your Git software, or in this particular case, the ssh program—Git literally runs ssh
here as Git does not contain the ssh code itself) provide to the server a public key: a long, random-looking string of letters and digits and perhaps other characters that represent some kind of cryptographic key. The public key is called this because it's not a secret! You may show it to literally anyone, although there's no good reason to bruit it about.
Along with this public key, whatever it is, there's a paired private key, which you should keep secret (guard it carefully, like a password). Given the public key only, anyone can easily encrypt any data; the holder of the private key can use the private key to easily decrypt that same data, but one who lacks the private key cannot.1 So when using ssh, you connect to the server—Bitbucket, in this case—and claim to be user git
and send them a public key. They use the public key to encrypt a randomly-chosen string, you use your private key to decrypt it and send that back, and they see that you are in fact in possession of the private key.
But: how will they know who you are? You claimed to be the git
pseudo-user. This is where the cheating comes in: every public/private key-pair has a unique public key.2 So, at some point—long before you use ssh to connect to the hosting server—you must connect to the hosting server with https
(and a user name and password, and perhaps 2FA) and upload to the hosting server the public key you plan to use later with your ssh connection.
At this point, the hosting server—Bitbucket, in this case—look in a big table or database that they have for all public keys. If the public key you just gave them is already in the table, they give you the error you saw:
Someone has already added that SSH key.
That someone could be you. In fact, assuming uniqueness of public keys, it must be you! They don't tell you who, they just tell you you cannot add this one now, because I, bitbucket, already have it. (Should you wish to find out who they think this particular public key represents, you simply have to run ssh -T [email protected]
: you have the private key too, and your ssh will use it to decrypt what they send, and you'll successfully log in, and they will tell you who they think you are.)
Assuming the key is not in their big table / database, they add it, along with the user name you've used to log in to their site using https. And now the hosting server knows that if someone—anyone!—comes along in the future and offers that public key, that "someone" is claiming to be you. If that someone can also decrypt the random string they offer as a challenge, that someone has the private key too, and therefore is you.
1Not, that is, without something deeply magical like a sufficiently powerful quantum computer. "Post-quantum" cryptography, as discussed in the linked article, is an attempt to defend against this future threat.
2This is not guaranteed in any technical sense, but enough extra data and other junk is included in the public key that in any practical sense, it will always be unique. Git itself has the same problem with object hash IDs: they're only unique in practice, not in theory. In theory, theory and practice are the same, but in practice they're not. —Benjamin Brewster, most likely
What happened in your case
It's impossible to be certain. Perhaps you're using https authentication for one repository, and ssh authentication for another.
You do, however, now know that you can run ssh -T [email protected]
to see who they think you are. This is based on the public key you offer them. If you need to have multiple accounts on Bitbucket, you'll need to carefully control which public keys you offer.
Note that ssh can use an agent to store key-pairs: this allows you to avoid storing the key-pairs directly on the computer you're using (e.g., if you're going through an intermediate work machine that's shared, you might want to keep your laptop-side keys on the non-shared laptop and use the agent to pass them through to the shared machine only briefly as needed). However, when you do use an agent, you lose some control over which keys are offered at which point in ssh negotiations. The .ssh/config
file lets you specify that only some particular key should be given out. You can also use this mechanism to say that your ssh should supply the pseudo-user git
user name: see, e.g., Bitbucket ssh public key is being denied but their ssh test connects with no issue (a question that dates back to the era when Bitbucket was still a Mercurial server, during the transition).