My thoughts on Slackware, life and everything

Tag: httpd

Fixing the cgit configuration of git.slackware.nl

In an earlier article here on the blog, I documented how I have setup cgit to create a browseable web interface to my git repositories, most notably git.liveslak.org and git.slackware.nl.

That configuration has worked just fine for years, and you could clone a branch of a repository using the “https://” git URI and that works.

However…
Cgit is a nice graphical shell around git repositories, but it does not serve the actual git protocol.
I found out when I tried to setup a mirror of the “current” repository on the Slackware Forge. For that, I needed to perform a full clone including all branches and tags. That failed mysteriously with the error:

$ git clone https://git.slackware.nl/current
Cloning into 'current'...
error: Unable to find d8220d28e5d53cd896b28d9dea13e2258923f35a under https://git.slackware.nl/current
Cannot obtain needed object d8220d28e5d53cd896b28d9dea13e2258923f35a
while processing commit 897670f2f2e1795652dfa3da21f9879d04a9b8dc.
error: fetch failed.

At first I thought that the remote repository was damaged or corrupted,but that was ruled out by searching for the hash to see if it would actually return something else than an error. From within the bare git repositorty on the server:

# git cat-file -t d8220d28e5d53cd896b28d9dea13e2258923f35a
commit

The hash does exist and it is a commit. That rules out the corruption.
Examining those errors “unable to find under ” and “cannot obtain needed object … while processing commit …” the conclusion is that the webserver only serves the bare repository as static files over plain HTTP, with no git-http-backend smart protocol behind it.
A working “git clone” over https needs the git-http-backend configured alongside the cgit configuration in Apache. Since that backend runs as the webserver user “apache”, and the git repositories are owned by user “git”,  we need to deal with file permissions/ownership and what git considers “safe directories”. Modern git (2.35.2 and later) refuses to operate on a repository owned by a different user than the one running git, and aborts with:
fatal: detected dubious ownership in repository at '/the/path/to/current'

The final configuration that adds the “git-http-backend” protocol to the Apache webserver and also resolves the “dubious ownership” error comes in two parts.

First:
A global git configuration directive which will only be used by the Apache user so that it won’t trip over the “git” user-owned repository files.
Create the file “/etc/httpd/http-git-config” and add these lines to it:

# ---
[safe]
        directory = *
# ---

Second:
Adding a code block to the VHost definition of the git server. In my previous article I described the git.liveslak.org configuration but actually this is identical to that for git.slackware.nl.
Therefore, to the file “/etc/httpd/extra/git.slackware.nl_content.conf” we add the following right before the “SetEnv CGIT_CONFIG” line:

# ---- smart HTTP for git clients (git-http-backend) ----
# Root directory that contains the bare repositories (current.git, etc.).
SetEnv GIT_PROJECT_ROOT /local/path/to/git/repositories

# Apache trips over the git:git ownership of the repositories, so we
# need to tell git that for Apache, all repositories should be considered
# as having safe ownership:
SetEnv GIT_CONFIG_GLOBAL /etc/httpd/http-git-config

# Route ONLY the git protocol paths to git-http-backend. Everything else
# (the cgit pretty URLs) is left untouched and handled by cgit below.
# The character classes accept both SHA-1 (40 hex) and SHA-256 (64 hex)
# object names, so this keeps working When we ever migrate hash formats.
# This MUST appear before the cgit ScriptAlias in the vhost.
ScriptAliasMatch \
  "(?x)^/(.*/(HEAD | \
    info/refs | \
    objects/(info/[^/]+ | \
      [0-9a-f]{2}/[0-9a-f]{38,62} | \
      pack/pack-[0-9a-f]{40,64}\.(pack|idx)) | \
    git-(upload|receive)-pack))$" \
  /usr/libexec/git-core/git-http-backend/$1

<Directory "/usr/libexec/git-core">
    Options +ExecCGI
    Require all granted
</Directory>
# -------------------------------------------------------

Test the Apache configuration for errors after saving your changes:

# apachectl configtest

And reload the webserver gracefully (not aborting current connections):

# apachectl -k graceful

Now, let’s test the server response when we send it a request:

$ curl -sI "https://git.slackware.nl/info/refs?service=git-upload-pack" | grep -Ei 'HTTP/|Content-Type'
HTTP/1.1 200 OK
Content-Type: application/x-git-upload-pack-advertisement

The content-type of “application/x-git-upload-pack-advertisement” instead of something like “text/plain” or “octet-stream” means that we have a properly working HTTP backend for git now. Cloning the repository no longer returns an error:

$ git clone https://git.slackware.nl/current
Cloning into 'current'...
remote: Enumerating objects: 103427, done.
remote: Counting objects: 100% (103427/103427), done.
remote: Compressing objects: 100% (31583/31583), done.
remote: Total 103427 (delta 69643), reused 103103 (delta 69319), pack-reused 0 (from 0)
Receiving objects: 100% (103427/103427), 759.01 MiB | 69.60 MiB/s, done.
Resolving deltas: 100% (69643/69643), done.

This fix allowed me to mirror https://git.slackware.nl/current/ to https://forge.slackware.nl/slackware/distrodevelopment with a sync frequency of 1 hour.

How I setup Apache for slackware.nl

Someone asked how I achieved the refreshed look & feel on slackware.nl and download.liveslak.org with the fancier directory listings and a graph of the current network bandwidth usage at the bottom.

It’s not so difficult but if you are new at setting up a web site mainly oriented at content delivery and have not worked with dynamic page generation using server-side includes (SSI), it may be useful to have some kind of reference.

So I decided to write up a short explanation on how I configured Apache httpd for the above two web sites. We’ll just imagine that you want to serve a slackware-current mirror from http://darkstar.home.arpa/slackware/slackware-current/

Bandwidth usage graph

Let’s first look what I did to generate that graph of the network bandwidth usage. Actually I stole the idea from slackware.uk where I noticed it for the first time. I had no idea what kind of tool Tadgy was using there, so I applied my Google-fu and found bwbar, a nifty little program written by H. Peter Anvin. I applied a few patches to the source code and built a Slackware package for it and installed this on my online server.
Next step is to decide where bwbar should write its bandwith usage data so that my Apache webserver can display that data. For the sake of this instruction, let’s assume a completely default unmodified httpd configuration. This means, web content goes into “/srv/httpd/htdocs/”. My customizations will also go into “/srv/httpd/” but I want to keep those files outside the htdocs directory. That’s just me, I want to keep content and presentation separated.
Further down, you will see that I use “Alias” statements in the httpd configuration to bind these directories into the htdocs content area (the so-called DocumentRoot).

I chose “/srv/httpd/pres/” as the location where the web server will find its presentation data. I want the ‘apache’ user to own that directory so that it can write the bwbar output there:

# mkdir /srv/httpd/pres
# chown apache /srv/httpd/pres

Then a commandline added to “/etc/rc.d/rc.local” will start bwbar during boot-up (as the apache user) and make it generate and refresh a graph (ubar.png) and accompanying text (ubar.txt) every 60 seconds:

# Start bandwidth reporting:
/bin/su - apache -s /bin/bash -c "/usr/bin/bwbar \
--output \
--directory /srv/httpd/pres \
--text-file ubar.txt \
--png-file ubar.png \
--width 600 --height 4 --border 1 \
--interval 60 \
--Mbps \
eth0 1000 & "

Once this commandline executes, you’ll notice it starts writing in “/srv/httpd/pres/”. It will keep running in the background and refresh the bandwidth usage data every 60 seconds.

Apache httpd.conf

Apache httpd configuration files are found below “/etc/httpd/”. Again, assuming a completely out-of-the-box installation, the following lines get added to the bottom of “/etc/httpd/httpd.conf”

# Use your own hostname here of course:
ServerName darkstar.home.arpa:80

# Load the module that enables server-side includes (SSI):
LoadModule include_module lib64/httpd/modules/mod_include.so

# We only actually need SSI inside the 'presentation' directory:
<Directory "/srv/httpd/pres/">
    # Make your .html page executable if you want httpd to parse it for SSI:
    XBitHack on
    Options +Includes
</Directory>

# This is where we will host our slackware-current mirror.
# We want every web page there to have a fancy header and footer,
# and the actual directory content inbetween:
<Directory "/srv/httpd/htdocs/slackware/">
    HeaderName /pres/HEADER.html
    ReadmeName /pres/FOOTER.html
</Directory>

# Our CSS files are outside the DocumentRoot so we need to bind them in:
Alias /css/ "/srv/httpd/css/"

<Location /css>
    Options -Indexes
    Require all granted
</Location>

# Our header, footer and bandwidth usage files are outside the DocumentRoot,
# so we need to bind them in:
Alias /pres/ "/srv/httpd/pres/"

<Location /pres>
    Options -Indexes +Includes
    Require all granted
</Location>

# Our icon files are outside the DocumentRoot so we need to bind them in:
Alias /icons/ "/srv/httpd/icons/"

<Directory "/srv/httpd/icons">
    Options Indexes MultiViews
    AllowOverride None
    Require all granted
</Directory>

# And then we add a whole bunch of icons to represent the content we may serve:
AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip

AddIconByType (TXT,/icons/text.gif) text/*
AddIconByType (IMG,/icons/image2.gif) image/*
AddIconByType (SND,/icons/sound2.gif) audio/*
AddIconByType (VID,/icons/movie.gif) video/*

AddIcon /icons/binary.gif .bin .exe
AddIcon /icons/binhex.gif .hqx
AddIcon /icons/tar.gif .tar
AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv
AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip
AddIcon /icons/a.gif .ps .ai .eps
AddIcon /icons/layout.gif .html .shtml .htm .pdf
AddIcon /icons/text.gif .txt
AddIcon /icons/c.gif .c
AddIcon /icons/p.gif .pl .py
AddIcon /icons/f.gif .for
AddIcon /icons/dvi.gif .dvi
AddIcon /icons/uuencoded.gif .uu
AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl
AddIcon /icons/tex.gif .tex
AddIcon /icons/bomb.gif core

AddIcon /icons/back.gif ..
AddIcon /icons/hand.right.gif README
AddIcon /icons/folder.gif ^^DIRECTORY^^
AddIcon /icons/blank.gif ^^BLANKICON^^

DefaultIcon /icons/unknown.gif

# Here is where it starts getting interesting.
# This 'IndexOptions' statement will already improve the look and feel of a directory index a lot:
IndexOptions FancyIndexing HTMLTable VersionSort NameWidth=*

# Some stuff we don't want to be visible to our visitors:
IndexIgnore .??* *~ *# HEADER* README.html RCS CVS *,v *,t favicon.ico

# And we want to spice up the index pages with some custom CSS (Cascading Style Sheets):
IndexStyleSheet /css/index.css

# Add a bit of metadata to our index pages to help people find them using online search;
IndexHeadInsert " \
<meta name=\"description\" content=\"Alien BOB's Slackware Linux mirror site.\"> \
<meta name=\"keywords\" content=\"Slackware,Slackware,Slackware Linux,alienBOB,alien\">"

Cascading Style Sheet (CSS)

This is some nice CSS which I found at https://www.apachelounge.com/viewtopic.php?p=21764 . Create a separate css directory to match the httpd.conf snippet above:

# mkdir /srv/httpd/css
# vi /srv/httpd/css/index.css

And add the following content into that index.css file:

body {
    background:#ffffff;
}

table {
    margin:10px auto;
    width:1000px;
    background:#edeeff;
    padding:20px;
    -moz-border-radius: 15px;
    border-radius: 15px;
}
table tr td {
    padding:2px;
}

h1#indextitle {
    margin:20px auto;
    width:990px;
    color:#001133;
    background:#edeeff;
    padding:20px;
    -moz-border-radius: 15px;
    border-radius: 15px;
}

h1#indextitle {
    font-size:20px;
    font-family: "Segoe UI", "Bitstream Vera Sans", "DejaVu Sans", "Bitstream Vera Sans", "Trebuchet MS", Verdana, sans serif;
}

tr.indexhead {
    font-size:16px;
    font-family: "Segoe UI", "Bitstream Vera Sans", "DejaVu Sans", "Bitstream Vera Sans", "Trebuchet MS", Verdana, sans serif;
}
tr.even {
    background:#fffff7; /* */
}
tr.odd {
    background:#edeeff; /* */
}

tr.indexbreakrow th hr {
    border:0;
    height:1px;
    background:black;
}

th.indexcolname {
    text-align:left;
}
th.indexcolname a:link,
th.indexcolname a:visited {
    color:#323B40;
    text-decoration:none;
}
th.indexcolname a:hover {
    color:#a81818;
    text-decoration:none;
}

th.indexcollastmod {
    text-align:center;
}
th.indexcollastmod a:link,
th.indexcollastmod a:visited {
    color:#323B40;
    text-decoration:none;
}
th.indexcollastmod a:hover {
    color:#a81818;text-decoration:none;
}

th.indexcolsize {
    text-align:right;
    padding-right:20px;
}
th.indexcolsize a:link,
th.indexcolsize a:visited {
    color:#323B40;
    text-decoration:none;
}
th.indexcolsize a:hover {
    color:#a81818;
    text-decoration:none;
}

th.indexcoldesc {
    text-align:left;
}
th.indexcoldesc a:link,
th.indexcoldesc a:visited {
    color:#323B40;text-decoration:none;
}
th.indexcoldesc a:hover {
    color:#a81818;
    text-decoration:none;
}

td.indexcolicon {
    text-align:center;
}

td.indexcolname {
    font-family:verdana;
    font-size:14px;
}
td.indexcolname a:link,
td.indexcolname a:visited {
    color:#323B40;
    text-decoration:none;
}
td.indexcolname a:hover {
    color:#a81818;
    text-decoration:none;
}

td.indexcollastmod {
    font-family: "Segoe UI", "Bitstream Vera Sans", "DejaVu Sans", "Bitstream Vera Sans", "Trebuchet MS", Verdana, sans serif;
    font-size:12px;
    text-align:center;
}
td.indexcolsize {
    font-family: "Segoe UI", "Bitstream Vera Sans", "DejaVu Sans", "Bitstream Vera Sans", "Trebuchet MS", Verdana, sans serif;
    font-size:12px;
    text-align:right;
    padding-right:20px;
}
td.indexcoldesc {
    font-family: "Segoe UI", "Bitstream Vera Sans", "DejaVu Sans", "Bitstream Vera Sans", "Trebuchet MS", Verdana, sans serif;
    font-size:12px;
    text-align:left;
    padding-right:20px;
}

Page header and footer

The HTML in the pageheader shows a descriptive text and ensures that the page content is nicely centered in the browser window.

Add the following to “/srv/httpd/pres/HEADER.txt”:

<hr width="100%">
  <center>
    <table>
      <tr>
        <td align="left">
        Welcome to SLACKWARE.NL!<br>
        This mirror service is provided by Alien BOB
        </td>
      </tr>
    </table>

The HTML in the footer uses server side includes, which gives the pages their dynamic nature; when the HTML is parsed, the actual bandwidth usage data is included on the spot.

Add this to “/srv/httpd/pres/FOOTER.html”:

<center>
<br>
<img src="/pres/ubar.png" title="<!--#include virtual='/pres/ubar.txt' -->" alt="<!--#include virtual='/pres/ubar.txt' -->" width=80% height=10 border=0>
<br>
<!--#include virtual="/pres/ubar.txt" -->
</center>

And then make this file executable (!) which tells Apache httpd to parse the HTML for SSI (that’s what that XBitHack statement was for):

# chmod +x /srv/httpd/pres/FOOTER.html

Add content

Create the directory “/srv/httpd/htdocs/slackware” and download a “slackware-current” mirror into that directory.

Tetst your config

The commmand “apachectl configtest” will show you any issues with your httpd.conf that need to be resolved before you can start the web server.

Start the web server

Make the rc script executable so that Apache httpd will start on every boot, and then start it manually this time:

# chmod +x /etc/rc.d/rc.httpd
# /etc/rc.d/rc.httpd start

Finally, go have a look at your new Slackware mirror site at http://darkstar.home.arpa/slackware/slackware-current/ !

Let me know if anything is not clear, or if the file content above was not copied correctly.
Have fun! Eric

© 2026 Alien Pastures

Theme by Anders NorenUp ↑