Up to Main Index Up to Journal for March, 2022 JOURNAL FOR FRIDAY 4TH MARCH, 2022 ______________________________________________________________________________ SUBJECT: Ad hock Git repository server with BusyBox DATE: Fri 4 Mar 19:25:17 GMT 2022 WARNING: This is one of my longer tutorial style posts I do now and again. It’s been a while since I did one of my little tutorials. I had cause to need this recently and thought I’d write it up in case anybody else could make use. Imagine you have one or more git repositories. Someone, somewhere needs read only access to clone and pull — maybe a co-worker, a friend, IT support, the great unwashed or a few ad hock Raspberry Pi in a “home lab”. How fast can you throw up a temporary, read only Git server for them to clone and pull from? How about a few minutes and 8 commands? Maybe 9 or 10 commands if you want to add a little basic security. First the quick and dirty: >mkdir -p webroot/gits >cd webroot/gits >git clone --bare /home/diddymus/gits/WolfMUD Cloning into bare repository 'WolfMUD.git'... done. >cd WolfMUD.git >mv hooks/post-update.sample hooks/post-update >chmod u+x hooks/post-update >git update-server-info >cd ../.. >busybox httpd -f -p 127.0.0.1:8080 -h gits First we create two directories ‘webroot’, for files we don’t want to serve, and inside that ‘gits’ where our repositories will reside. The we clone our first bare repository into ‘gits’. The Git post-update hook is made active by renaming it and making it executable. Then the information Git needs for the dumb protocols to work is manually updated — this is done automatically after setup by the post-update hook on subsequent updates to the repository. Finally we return to the ‘webroot’ directory and start the BusyBox httpd server. Once the BusyBox httpd server is started the repository can be cloned/pulled from like normal: >mkdir sandbox >cd sandbox >git clone http://127.0.0.1:8080/WolfMUD.git >cd WolfMUD >git branch -av * dev a56d2a5 Merge branch 'dev' into go1 remotes/origin/HEAD -> origin/dev remotes/origin/dev a56d2a5 Merge branch 'dev' into go1 remotes/origin/go1 a56d2a5 Merge branch 'dev' into go1 >git remote -v origin http://127.0.0.1:8080/WolfMUD.git (fetch) origin http://127.0.0.1:8080/WolfMUD.git (push) > When you have finished just Ctrl-c on the BusyBox httpd server. If you want you can clone multiple repositories into the gits directory and make them all available. It goes without saying that if you have access to the repositories via another means, for example ssh, you have full normal access and can push updates that others can then pull or clone. There is a drawback to this method, the only security is via obscurity — if you know the correct IP address, or domain, and port number you can access the repositories. There is also the fact we are using HTTP. However, this is a very simple setup and because of that you don’t need to have a domain or SSL certificates for this method to work. Can we add a little security? We can do better, but it requires a configuration file for httpd — which I usually name httpd.conf :) Be warned, we will still be using HTTP. Here is an example httpd.conf that would go into the webroot directory: # busybox httpd -f -c ./httpd.conf -p 8080 A:127.0.0.1 D:* H:./gits /:diddymus:wolfmud The comment is me being lazy and not remembering all of the options used to run httpd :/ The ‘A:127.0.0.1’ says to allow traffic from localhost and ‘D:*’ denies traffic from everywhere else. The ‘H:./gits’ specifies the document root to be served. The ‘/:diddymus:wolfmud’ adds basic authentication for user ‘diddymus’, password ‘wolfmud’. If you don’t fancy the password being in the clear then a hash can be generated for it: >busybox httpd -m wolfmud $1$JMPCQ2g3$04wyKV2Az.Vh/qyY9HRWZ1 Our configuration would then be: # busybox httpd -f -c ./httpd.conf -p 8080 A:127.0.0.1 D:* H:./gits /:diddymus:$1$JMPCQ2g3$04wyKV2Az.Vh/qyY9HRWZ1 It’s not recommended, but it’s possible to require a user without password: # busybox httpd -f -c ./httpd.conf -p 8080 A:127.0.0.1 D:* H:./gits /:diddymus: Using the configuration above that does require passwords — the repository can be cloned using any of the following URLs: >git clone http://127.0.0.1:8080/WolfMUD.git Cloning into 'WolfMUD'... Username for 'http://127.0.0.1:8080': diddymus Password for 'http://diddymus@127.0.0.1:8080': > >git clone http://diddymus@127.0.0.1:8080/WolfMUD.git Cloning into 'WolfMUD'... Password for 'http://diddymus@127.0.0.1:8080': > >git clone http://diddymus:wolfmud@127.0.0.1:8080/WolfMUD.git Cloning into 'WolfMUD'... > The difference is in how much detail you specify on the URL to clone and how much Git will remember. If you specify just the URL then Git will ask for the user and password every time. If you specify the user on the URL Git will only ask for the password. If you specify the user and password then Git is silent. For example: # after: git clone http://127.0.0.1:8080/WolfMUD.git >git pull Username for 'http://127.0.0.1:8080': diddymus Password for 'http://diddymus@127.0.0.1:8080': Already up to date. > # after: git clone http://diddymus@127.0.0.1:8080/WolfMUD.git >git pull Password for 'http://diddymus@127.0.0.1:8080': Already up to date. > # after: git clone http://diddymus:wolfmud@127.0.0.1:8080/WolfMUD.git >git pull Already up to date. > If you use one method and later change your mind you can update the URL used with Git’s remote command. For example if you cloned the repository and only had Git remember the user, but now want Git to remember the password as well: >git remote -v origin http://diddymus@127.0.0.1:8080/WolfMUD.git (fetch) origin http://diddymus@127.0.0.1:8080/WolfMUD.git (push) >git remote set-url origin \ http://diddymus:wolfmud@127.0.0.1:8080/WolfMUD.git >git remote -v origin http://diddymus:wolfmud@127.0.0.1:8080/WolfMUD.git (fetch) origin http://diddymus:wolfmud@127.0.0.1:8080/WolfMUD.git (push) > You can use Git’s credential helpers which can either store the credentials for you unencrypted on disk or store credentials for a period of time in memory. For example to cache credentials in memory for 15 minutes (900 seconds) we can use: >git remote set-url origin http://127.0.0.1:8080/WolfMUD.git >git config credential.helper "cache --timeout 900" >git pull Username for 'http://127.0.0.1:8080': diddymus Password for 'http://diddymus@127.0.0.1:8080': Already up to date. >git pull Already up to date. > First we set the remote to just be the URL with no credentials, so Git will ask for the username and password. Then we set the credential helper to use the cache helper to keep credential in memory (in this case for 15 minutes, which is also the default if --timeout is not specified). Then we test using ‘git pull’. The first time the credentials are asked for and the second time the credentials are retrieved from the cache. Note that the timeout is from the last time the credentials were last used and not when first cached. Getting back to our BusyBox httpd.conf file… You can add multiple ‘A:’ and authentication lines to the configuration. For example, assume I setup two repositories WolfMUD.git and theCottage.git. I want to give Alice access to WolfMUD.git and Bob access to theCottage.git. Alice will be connecting from 172.16.1.1 and Bob connecting from 172.16.1.2: # busybox httpd -f -c ./httpd.conf -p 8080 A:172.16.1.1 A:172.16.1.2 D:* H:./gits /WolfMUD.git:alice:goldfish /theCottage.git:bob:herring This locks down access to the specific IP addresses and users with passwords. For clarity I didn’t hash the passwords, otherwise the end of the file would be: /WolfMUD.git:alice:$1$IJEQVljI$henoDi6JVNk3Xu4WDGdcK1 /theCottage.git:bob:$1$6hwcmY6s$vlZTB64o2cVlieRGy1jBW0 Instead of specifying both IP addresses you can use just ‘A:172.16.1’ or CIDR notation ‘A:172.16.1.0/24’ to include all IP addresses in the range 172.16.1.1 to 172.16.1.254. So what are the benefits of being able to do this? • Simple to setup quickly • Very light on resources[1] • It’s read only and could be exposed to the public • Easy to chroot using a static BusyBox build • Being a single binary this can quickly be setup in a container • You do not have to be root[2] • Works without a domain, with a domain or dynamic DNS services • Works well with dynamic IP addresses (just restart BusyBox httpd) • BusyBox httpd can be daemonized and run in the background[3] Can you really not use HTTPS/TLS with httpd? Well… you can, take a look at TLS proxies such as Ghostunnel or stunnel — but then you lose the ‘simple’ aspect. Bonus example for reading this far! :) If you have a static build of BusyBox[4] available, running the server in a chroot environment is very easy: >cd webroot >cp -a /usr/bin/busybox . >ls busybox gits httpd.conf >sudo chroot --userspec=diddymus:diddymus . \ ./busybox httpd -f -c ./httpd.conf -p 8080 If you omit the ‘-f’ flag to httpd the server will daemonize and run in the background. Another amendment your users might like is a simple menu of what is available, you’re already running a web server. Just drop an index.html into the ‘gits’ directory: <!DOCTYPE html> <html> <head> <title>Git Repositories</title> <style> body, h1 { text-align: center } main { text-align: left; display: inline-block } code { line-height: 2em; } </style> </head> <body> <main> <h1>Available Git Repositories</h1> <dl> <dt>WolfMUD</dt> <dd>Open source, multiplayer, networked, text adventure game.</dd> <dd><code>git clone http://127.0.0.1:8888/WolfMUD.git</code></dd> <br /> <dt>The Cottage</dt> <dd>Simple JavaScript plaything.</dd> <dd><code>git clone http://127.0.0.1:8888/theCottage.git</code></dd> </dl> </main> </body> </html> Then you just need to tell your users the IP address/domain of the server and the port number. Browsing there would then show something like: >lynx --dump http://127.0.0.1:8888/ Available Git Repositories WolfMUD Open source, multiplayer, networked, text adventure game. git clone http://127.0.0.1:8888/WolfMUD.git The Cottage Simple JavaScript plaything. git clone http://127.0.0.1:8888/theCottage.git > Of course you can jazz up the HTML any way you want. Maybe add any pertinent instructions? One of those nifty icons next to each command to copy it to the clipboard? The HTML could even be generated by a script that then starts the httpd server running. You could even update the ‘description’ file in each repository for the script to read ;) -- Diddymus [1] I’ve been running the examples and doing a lot of repository cloning and pulling. The ‘top’ command says the busy box process is using 2.5Mb RAM and 1.2Mb is shared… [2] Unless you want to bind to a privileged port, in the range 1024 and below, like port 80[5]. Or you want to run httpd in a chroot. [3] Turns out this need not be just a quick temporary solution… ;) [4] I’m using the busybox-static 1:1.30.1-7+b2 package on Debian for these examples. [5] You can add your user to /etc/security/capability.conf and use Linux’s capabilities. Or systemd service units and AmbientCapabilities :P Up to Main Index Up to Journal for March, 2022