Publish a Read-Only Git Repository Browser with gitweb
You will configure gitweb to browse repositories under one filesystem root, verify that the CGI script can find them, and restrict visibility before putting the interface behind a web server. The installed documentation is from Git 2.43.0, packaged here as git-man 1:2.43.0-1ubuntu7.3.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 20 minutes for an existing CGI-capable web server, or about five minutes for the local smoke test. You need Git, Perl, a bare repository or repository object database, and administrator access only for files under /etc or the web server configuration. This guide does not publish a service or open a firewall port for you.
1. Confirm the installed Git and gitweb files
Start with read-only checks. They do not need elevated privileges:
$ git --version
git version 2.43.0
$ command -v perl
/usr/bin/perl
$ ls -l /usr/share/gitweb/gitweb.cgi
-rwxr-xr-x 1 root root ... /usr/share/gitweb/gitweb.cgi
The exact permissions and file timestamp can differ. The useful checks are that the Git version is the one you intend to run and that the CGI script exists. The CGI script needs an absolute Git executable path in some web server environments, so the configuration below sets it explicitly.
Checkpoint: do not continue if the web server will execute a different Git installation from the one you just checked. Multiple Git versions can produce confusing results when the CGI script and command-line tools inspect the same repositories.
2. Try a private local instance first
From a repository you are allowed to browse, use git instaweb for a temporary local test:
$ cd /path/to/working/repository
$ git instaweb --local --httpd=python --port=1234
$ git instaweb --stop
--local binds the server to 127.0.0.1, and --port selects the listening port. The command starts a small web server and writes per-repository instance state, so it is not a substitute for a managed deployment. The final command stops that instance. If the start command reports that the selected server is unavailable, use a supported server installed on the machine or stop here and fix the local test first.
Do not omit --local on a machine where the test repository contains private source. A successful browser page proves that the local wrapper can launch gitweb; it does not prove that a production web server has correct access controls.
3. Set the repository root in gitweb.conf
Gitweb reads Perl statements from a common system file, then a per-instance file or the fallback /etc/gitweb.conf. Later files override earlier values. Put only trusted administrator-maintained code in these files: they are sourced as Perl, not parsed as passive data.
Create or edit the fallback configuration as root:
$ sudoedit /etc/gitweb.conf
For a simple read-only browser, use:
our $projectroot = '/srv/git';
our $GIT = '/usr/bin/git';
our $site_name = 'Example Git';
$projectroot is prepended to each project path. With this setting, a project named team/app.git maps to /srv/git/team/app.git. When $projects_list is unset, gitweb scans the root for Git object databases, not working trees. That makes bare repositories a good fit for hosting.
Check the file as Perl before asking a web server to load it:
$ sudo perl -c /etc/gitweb.conf
/etc/gitweb.conf syntax OK
This catches Perl syntax errors only. It does not check that the directory exists or that the web server account can read the repositories.
4. Verify discovery with filesystem and repository checks
Use ordinary commands to check the paths before testing through HTTP:
$ test -d /srv/git && echo 'repository root exists'
repository root exists
$ test -d /srv/git/team/app.git/objects && echo 'object database found'
object database found
$ sudo -u www-data test -r /srv/git/team/app.git/HEAD && echo 'web account can read HEAD'
web account can read HEAD
Replace www-data with the account used by your web server. The last check may need elevated privileges because it tests another account, but it changes nothing. If it fails, fix ownership, group membership or permissions deliberately. Do not make the entire repository world-writable, and do not grant the CGI account write access merely to make browsing work.
Checkpoint: confirm that gitweb.cgi, its configuration, the repository root and the web server account all refer to the same paths. A blank project list is often a path or permission problem, not a Git history problem.
5. Restrict the projects that can be shown
Filesystem scanning is convenient but broad. For a host with a known set of repositories, replace it with an explicit project list. Each line contains a repository path relative to $projectroot, followed by a URI-encoded owner:
team/app.git Admin+Team+<[email protected]>
tools/build.git Build+Team+<[email protected]>
Spaces separate fields, so spaces and plus signs in field values need the encoding described by the manual. Configure the list and make the list authoritative for access, not merely for the overview page:
our $projectroot = '/srv/git';
our $projects_list = '/etc/gitweb-projects';
our $strict_export = 1;
With $strict_export true, a repository absent from the list cannot be viewed by hand-crafting its URL. Without it, a project list can hide a repository from the overview while leaving it accessible if someone knows the URL. Treat this distinction as an access-control boundary, not a presentation preference.
For another explicit boundary, set $export_ok to a marker file name. Gitweb will show a repository only when that file exists inside its Git directory:
our $export_ok = 'gitweb-export-ok';
Adding or removing that marker changes which repositories are available, so plan it as an administrative change. Keep a copy of the previous configuration and list if you need to undo the change:
$ sudo cp --preserve=all /etc/gitweb.conf /etc/gitweb.conf.before-export-change
$ sudo cp --preserve=all /etc/gitweb-projects /etc/gitweb-projects.before-export-change
To recover, restore those copies after checking their contents, then reload the web server only if its configuration requires a reload.
6. Test the CGI path and the web server boundary
Configure your web server to execute /usr/share/gitweb/gitweb.cgi as CGI, following that server's package documentation. The gitweb manual's Apache example uses a ScriptAlias and enables CGI execution for the directory. Keep the CGI endpoint behind your normal TLS, authentication and network policy; gitweb itself is a repository browser, not an identity provider.
Open the endpoint without a repository parameter. The default action is the project list. Then open one listed repository and check its summary, log and file views. A repository parameter normally identifies the path relative to $projectroot, and the default revision is HEAD.
If the project list is empty, check these in order: the CGI process is reading the configuration file you edited, $projectroot is absolute and correct, the repository has an object database, and the web account can traverse every parent directory. If a listed project is still accessible after removing it from the list, check that $strict_export is set and that the running instance is not using a different per-instance configuration.
Do not enable the blame feature merely to make the interface look complete. The manual says it is disabled by default for performance reasons. Enable optional features only after measuring their cost and reviewing the access implications.
Done means
- Git 2.43.0 and the intended gitweb CGI script are identified.
- A local
git instaweb --localsmoke test was stopped after use. $projectroot,$GITand the site name are in a trusted Perl configuration file.- The web account can read the intended repository without write access.
- An explicit project list and
$strict_exportare used where hiding a repository must also prevent direct access. - The CGI endpoint was tested through the real web server, with TLS and authentication handled by that deployment.