A Web Frontend For Git
This idea occured to me a year ago when I figured out how you can push to remote Git repository through SSH. I've always been planning to quote-unquote "liberate my infrastructure" by replacing them with self-made software; making my own code repository hosting software would be one of the important steps of this. Absurdly, when I started writing the majority part of this blog post I was actually genuinely trying to switch to Fossil; now I've went back to using Git, whatever I'm doing can now at least have one (1) user.
The reason why I've decided to choose this instead of other possible things (some of which, of course, are on my to-do list) was that I used to self-host Git on my VPS and was not satisfied with GitLab (too resource intensive) and Gitea (not quite I'd want); I also had a fervent need to avoid software made by a certain left-wing grifter despite what he did was truly impressive. Also, probably the most important reason why I'm doing this is that I now believe having a set of open source, ideologically-neutral infrastructure is extremely important, especially at a time when every single big-name open source project seems to be secretly run by people who're at least somewhat sick in the head. The next step would be making an ideologically-neutral Redis. The very first version of Redis (the one written in Tcl) is actually very simple; I have a good amount of hope for this…
Prelude: "Knowing Git" versus "Knowing Git"
How far should you know Git? It feels like that whichever skill they're expecting from you when you're job searching they're only using it to shanghai you into admitting that you're less skillful than you actually are so you would accept a lower wage. Everyone needs to know about Git now, but how deep should you know? I know how to use low-level plumbing commands now, does that count as "good at Git"? There's a long-standing joke among the Chinese that you should just bluff on your resume: if you've worked through a few tutorials you'd say you're good at it, and if you've done a few more you'd say you're an expert. Is there really any meaningful endgame to this? I want off this ridiculous train, this arm race of bluff.
Future potential employer who's reading this: I know way around things and I know how to know my way around things (as shown here; all the discoveries I've made I made them on my own), for the love of Christ, please give me a chance…
How push/pulling via SSH works for Git hosting services
There has always only been one user from the view of the server; much like a web forum app, the multiple "Git user" is merely an illusion supported by some random databases. It should be obvious from the fact that we don't get shell accounts automatically from GitHub when we first register for their service, but it somehow isn't obvious why I haven't realized this sooner. If you ever push/pulled from a remote source via SSH, their URLs almost always have a git@ part which is obviously how you specify the username in an URL; you are not communicating with the server directly, it is always git who acts on your behalf.
As mentioned above, I have a Fossil server setup (see my old blog post on this topic); I was trying to disable the shell for the Fossil user because having the dedicated user being this "open" is obviously bad feng shui, but I didn't want to do the changes directly on the Fossil user, so I created a test user and changed its shell to /sbin/nologin; me being the kind of guy whose SSH knowledge only consists of how to login and how to upload/download files via SFTP was slightly surprised by the effect:
- I couldn't SSH (which was well expected)
- I couldn't SFTP (which was slightly less expected)
- I couldn't SSH with custom command (which was not expected)
Here comes the thing: Git client works with GitHub and is able to pull & push things; the actions of push & pull are actually done by the Git client issuing special commands via SSH; but GitHub is still able to stop its users from directly logging into the shell. This means that there is a special kind of shell at play and it is not /sbin/nologin. A few searches revealed that git-shell is a thing so that part of the mystery is solved.
Gitolite pretty much does what I think these kind of programs do: (1) it relies on the authorized_keys file; (2) it discerns different public keys by passing the username (username within the Git hosting system) as an argument, since authorized_keys allows a command argument being specified; (3) because its functionality fully relies on sshd and the authorized_keys file, it would need a way (and thus have a way) to somehow manage and update the file whenever there's new public keys being added and old public keys being deleted. Gitolite uses a special Git repository to manage public keys and other things like permissions; the authorized_keys file is probably updated via Git hooks. I did not know that SSH_ORIGINAL_COMMAND would become a must for it to play nicely with the Git client, since when logins (initiated by the client) are happening the commands the Git client issues is ignored and got replaced by the command arguments from the authorized_keys file.
The whole process is as follows:
- The human-side user (i.e. a human or anything that acts on the side of human user, e.g. automation scripts) must be able to quote-unquote "login" with the shell user
gitthrough SSH; this requires the public key to be installed into the~/.ssh/authorized_keysfile of shell usergitbeforehand.- When login via SSH, if a
commandargument is configured in theauthorized_keysfile, that command (or the specified command from the human-side, if there is any) would be executed with the shell usergit's shell. This command can be different according to the actual public key being used for login (!). The "original" command, either the default shell or the specified command, is stored in the environment variableSSH_ORIGINAL_COMMAND. As mentioned before, Git via SSH issues special commands; this would get stored in the env var.
- When login via SSH, if a
- Git connects through SSH and attemps to login with pubkey. If the login is successful, Git would issues special commands (which would be stored in the environment variable
SSH_ORIGINAL_COMMANDwhen the corresponding command configured inauthorized_keysgot invoked.- The command configured in
authorized_keysshould execute a special program using the user name as (part of) the arguments;
- The command configured in
- The special program then retrieves the username via command line arguments and the original intention via
SSH_ORIGINAL_COMMAND; now, depending on the configured permission of different usernames, the program shall perform the action or reject accordingly.
So, to summarize:
- One single shell user,
git;- Can be other name but you should have a dedicated one.
- Special-purpose shell at play;
git-shellin this case;- But not
git-shelldirectly; we need a wrapper that takes an argument (which would be the username) and can retrieve the original intention via env varSSH_ORIGINAL_COMMAND; - This special-purpose shell can be done in two ways:
- "You become the shell itself", in which case the dedicated
gituser would be set to use the special shell program as their shell using things likechshand you have to manually check for Git Shell special commands. - "You become a plugin", in which case the program must reside in folder
$HOME/git-shell-commands, and you still have to manually check for Git Shell special commands yourself since the control do be handed over to you when they SSH.
- "You become the shell itself", in which case the dedicated
- But not
- The association between usernames and their public keys can be stored elsewhere, but has to be in the
authorized_keysfile of the usergit;
Per-repository permissions
I would like to see how vanilla Git handle permissions first, so I set up a virtual machine and did a bit testing:
- Some favours of Linux installations would come with a Git user setup already. The user is commonly named
git; one should check if such a user exists before creating a new one. (Some software, e.g. Gitolite, would create a new user regardless.) - By adding a
~/.ssh/authorized_keysfile to the Git user's home directory, one can try to interact with repos with SSH remote URLs. It doesn't matter who owns the file on the server, as long as the file exists and the SSH key is in the file, you can try topushand it will ask for your private key. If this is not set up, you'll have to login as the git user, which you typically wouldn't be able to do. - You still wouldn't be able to push to remote after setting up
authorized_keys. It seems like if you don't log in with the password of the Git user the remote server would have no idea who you are and would refuse your access. - The supporting command,
git receive-pack,git upload-packandgit upload-archiveseems to not have any permission control mechanism.
These observations leads to the following conclusion:
- You do have to implement the permission control in your
git-shellwrapper. - The path part of the remote URL is passed as an argument of the supporting commands, and thus it's also a part of
SSH_ORIGINAL_COMMAND.
I wrote a "test wrapper" in Nim that displays whatever's in SSH_ORIGINAL_COMMAND and then exists; and when I tried to use it I found out a few more things:
- You need to use the
commandargument in theauthorized_keysfile, but if the shell of your Git user is stillgit-shellthis wouldn't work; it seems like the accurate order of things is that the shell of the Git user (the one you configure usingchsh) runs first, and then thecommandargument got executed within whatever's provided by that shell.git-shelldoes support custom commands by having a~/git-shell-commandsfolder and putting the executable there. - The output needs to follow Git's wire protocol, documented here and here. A version 2 also exists and could be used by certain clients.
What happens to this project nowadays
The project is now hosted on GitHub: https://github.com/AegisCodeDepot/aegis. I did a bit more work, including implementing a very crude form of pull request. I made a video on this, thinking people would like to watch videos on big difficult-sounding projects; turns out that wasn't the case. Truly strange to me - I see videos on hard topics and mundane topics by other people and they seems to get views. I suppose this is it…
Last update: 2025.7.11