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