Hello there!
Protocols supported by git
git
supports several protocols to get changes from a remote server or send your changes back to the server.
ssh
It’s the best option available. Especially, if you constantly use ssh
to login to a server. Yeah, it requires some basic configuration create an SSH key, send your public key to a server, and configure the git
user on a server (shell, permissions). But you need to do it only once. After that, it should work smoothly. It’s “natively” supported by git
, so you don’t need any special configuration from the git
side. Just use something git@server.com/path/repo.git
. But this article is not about ssh
, so I’m not going to explain all details of ssh
.
git
It’s another protocol supported by git
, implemented in git daemon
. This protocol uses the 9418 port and exposes any repository that has the git-daemon-export-ok
file in it. One major drawback of this protocol: it doesn’t support any authentication. So, you can use it for read-only repos but it might be a bad idea to allow write permission to everyone.
”Dumb” http
The “dumb” http
protocol is absolutely the same as the simplest way to host some files over http
. It is the first implementation of http
in git
and it is called “dumb” because it doesn’t provide any logic on top of http
. It relies on serving static files nothing more. This protocol is only suitable for repository cloning. You can’t push your changes back to a server using this protocol.
”Smart” http
The “smart” http
protocol works closer to ssh
. It involves the execution of git
logic on the server side. So, the http
protocol is just a transport to send/receive data. There are several versions of the “smart” http
protocol, the latest one is 2. And usually, this protocol is more efficient because git
can decide what to send and how to pack it. When you need to get some changes from a server, instead of sending them as static files, git
executes special commands on the server and sends results over http
. There are several different biniries involved in this process but the main one is git-http-backen
.
git http-backend
This command allows implementing the “smart” http
server. It’s just a binary and is expected to be used by any HTTP server (Apache
, nginx
, etc.) as a CGI script. On each request, the server sets some environment variables like REQUEST_METHOD=GET
and starts git-http-backed
. If it is a GET
request, then git-http-backend
processes the request and outputs the result into stdout
. If it is a POST
request, then git-http-backedn
expects the request’s body to stdin
.
You can easily find a configuration example for Apache
or nginx
. This configuration is pretty simple, probably, because git-http-backend
is designed to be used by these servers. But git-http-backend
doesn’t have a lot of documentation on how it works internally and which parameters it expects. So, in this article, I will provide a configuration to host any git
repository in ASP.NET Core.
ASP.NET Core as a proxy
Let’s start with simple stuff. If you want to host a git repository, you need to have one. So, let’s assume you have created a new repo by git --bare init repo.git
. It’ll create a bare git repository - the repository without a working tree, just a .git
folder. Usually, you don’t need a working tree on a server.
Also, here is a list of all required environment variables:
GIT_HTTP_EXPORT_ALL
- it’s a marker variable and the value doesn’t matter, it allows to host any repository, otherwisegit
will be looking forgit-daemon-export-ok
file in each repository.HTTP_GIT_PROTOCOL
- the version of thegit
protocol, you can hardcode this value or get it from request headers.REQUEST_METHOD
- the http method,GET
orPOST
.GIT_PROJECT_ROOT
- the absolute path to your git repository.PATH_INFO
- the relative path of the request, for example, if the client requestedhttp://localhost/repo.git/info/refs?service=git-receive-pack
, thenPATH_INFO
should be equal toinfo/refs
.QUERY_STRING
- the query string from the request without the?
symbol, for example,service=git-receive-pack
.CONTENT_TYPE
- the content type header from the request.CONTENT_LENGTH
- the content length header from the request.HTTP_CONTENT_ENCODING
- the content encoding header from the request.REMOTE_USER
- the user name.REMOTE_ADDR
- the ip address from the request.GIT_COMMITTER_NAME
- the user name.GIT_COMMITTER_EMAIL
- the user email.
Let’s look at some code. Here is an endpoint to handle all requests related to git
.
And here is the class with the logic. It might look like a lot of code but in fact, it is pretty simple. The first several lines are responsible for the startup of git-http-backend
and env vars configuration. And then we have two methods. The first one is to read the body of the request and send it to stdin
. The second one is to read headers from the git-http-backend
response and set them to our ASP.NET Core response, also it reads the body of the response and sends it to the end client. I decided to use System.IO.Pipelines
to send/receive data between ASP.NET Core and git-http-backend
, it might be overkill but I thought it would be easier to part headers by using SequenceReader
.
This code is just a “proxy” between ASP.NET Core and git-http-backend
.
So far so good. Everything should work with this code at least clone and fetch/pull but not the push.
Authentication/Authorization
To add the push operation support, we need to implement Authentication/Authorization
. Yeah, if you don’t care about it and want to allow push for everyone, then you can run git config http.receivepack true
or add http.receivepack
manually to your config
file. It will allow an anonymous push. If you still need to implement proper Authentication/Authorization
then you need to add Basic HTTP Authentication. Unfortunately, the latest version of ASP.NET Core doesn’t support it from the box (I guess because of security reasons). So, we need to implement it manually or use some nuget package. Fortunately, the algorithm of Basic HTTP Authorization is pretty simple and we can implement it by ourselves. TLDR: you need to get a base64-encoded string from the Autorization
header, decode it, and split it by :
. The first part is the login and the second part is the password.
Here is the registration of our Basic
scheme and handler for it, alongside JWT
.
The implementation of Basic HTTP Authentication
:
Please pay attention to the WWW-Authenticate
header, it is important. Also, this code doesn’t have a proper validation of user credentials, it is hardcoded to admin/admin
combination. You need to implement your own credentials validation and add all necessary claims to your identity.
If everything is configured correctly, now you will be able to do all necessary actions with a remote repository: clone, fetch, pull and most importantly push.