authentication and authorization in gitolite

(viewing this slideshow)

This presentation uses HTML Slidy, a simple presentation software from the W3C. Although there’s a help button in the footer of each presentation, it’s missing some important stuff, so here’s a smaller but more complete summary of the keyboard controls.

Navigation

  Next slide: right arrow, page down, space
  Prev slide: left arrow, page up
  Down within slide: down arrow
  Up within slide: up arrow
  First slide: home
  Last slide: end

Display

  Smaller font: “S” or “<” key
  Larger font: “B” or “>” key
  Toggle Current versus All slides: “A” key
  Toggle Table of Contents popup: “C” key
  Toggle footer: “F” key

To search for stuff in the full document using your browser’s Ctrl-F, first view all slides (press the “A” key).

authentication versus authorisation

Before we start, we need to be clear about the difference between authentication and authorisation.

Authentication

  • checks user’s identity (“who are you?”)
  • is NOT done by gitolite!
    • although gitolite does help to set it up if you’re using ssh
  • is done by ssh server (or perhaps by http server if you are running gitolite in http mode)

Authorisation

  • check’s user’s access (“now I know who you are, are you allowed to do what you want to do?”)
  • This is what gitolite does

git over plain ssh

First, we’ll recap how git works over plain ssh.

The client user authenticates herself to the server using any method supported by ssh – password or ssh public key. It does not matter to either the git client or the git server how that happens; that’s entirely between ssh and sshd (the ssh daemon or server).

There is no authorisation in this mode, other than any file system permissions that the OS may enforce.

how does gitolite work?

Gitolite adds an extra layer in between the sshd and the git-receive-pack (or git-upload-pack, for read operations), to check if the access is allowed and abort if needed. Gitolite also installs its own update hook (see man githooks) in every repository to check branches being pushed.

This means gitolite does two authorisation checks, as you will see.

Gitolite also requires that the authentication must happen using an ssh public key. We’ll see why as we go along.

Here’s a series of pictures that show how gitolite works. We’re using a “git push” example; the only difference for a fetch or clone is that instead of git-receive-pack it’d call git-upload-pack, and the last check (update hook) does not apply.

Terminology reminder: “ref” in git parlance means “branch or tag”. At least in this page that’s what we mean :-)

the diagrams…

The diagrams were originally drawn to be self-contained. On a first pass you can simply ignore the text on each page, and come back to it later for the finer points.

Experts: yes, I know I simplified the ssh part a lot. If you can find fault with these pictures, you’re not the target audience :-)

When the user runs a “git push” command, the git client calls ssh, passing it a command like

git-receive-pack 'reponame'

The git client, before it starts talking to the server, looks for the user’s ssh keys so it can use public key authentication (and not ask for a password).

As you know, for gitolite to work, the user must already have an ssh key pair and the pub key already sent to the gitolite administrator who should have added it to gitolite (which in turn means it’s part of ~/.ssh/authorized_keys on the server).

The ssh client sends the public key to the server to identify itself.

The server looks for the public key in its “list of people who’re allowed in” (~/.ssh/authorized_keys). If it finds it, things are good.

If it doesn’t find it, it tells the client and the client asks the user for a password. Which is BAD, because Gitolite doesn’t work with passwords!

If authentication succeeds, the ssh server calls gitolite-shell, with the username as the first argument. Here’s how that happens.

When the administrator adds Alice’s pubkey to gitolite, he names it “alice.pub”, puts it in the “keydir” directory of a clone of the gitolite-admin repository, and pushes the new file to the server.

When the Gitolite server receives that push, one of the things it does is add this key to ~/.ssh/authorized_keys file, but prefixed with a bunch of options, one of which is a “command” option that looks like this:

command="/home/git/bin/gitolite-shell alice"

It is this “command” that the ssh daemon executes when the offered public key matches the public key in this line. For a different public key, from a different user, the command will have that user’s name instead of “alice”.

This is how Gitolite distinguishes users from each other, even though they’re all accessing the same (git@host) account.

This is also why passwords won’t work. When a user supplies a password, there’s no additional information to help the ssh server distinguish one user from another.

When the ssh daemon decided to execute /home/git/bin/gitolite-shell alice instead of the git-receive-pack 'reponame' command that the user’s git client sent to the user’s ssh client, it didn’t simply discard the git-receive-pack command. It put it in an environment variable called SSH_ORIGINAL_COMMAND.

Since that variable contains the repository name, and the username is the first argument passed to it, gitolite-shell now has enough information to decide “is this user allowed to write to this repo” (or “read”, if the command was git-upload-pack).

Assuming access is allowed, gitolite-shell then calls git-receive-pack (or git-upload-pack, as the case may be).

This is the first authorisation check that gitolite does.

Notice that git-receive-pack and git-upload-pack have NO idea that someone snuck in between them and the client calling them. Neither does the git client know. Gitolite is totally transparent to git clients unless access is denied for some reason.

For a read (git-upload-pack), that is the end of the story.

For a write, git will invoke the update hook for each branch or tag pushed. (See man githooks for details).

This update hook, of course, has been installed by gitolite, and it proceeds to check if

  - this user (alice, bob, …)
  - is allowed to do this (create, fast-forward push, non-fast-forward push, or delete)
  - to this branch or tag (master, refs/tags/v1.0, …)
  - in this repo.

This is the second authorisation check that gitolite does.

If it is not allowed, the update hooks exits with an error, and git will abort the push (for that ref; other refs sent in the same push may be OK).