diff options
| author | RATDAD <lambda@disroot.org> | 2025-12-13 21:00:08 -0500 |
|---|---|---|
| committer | RATDAD <lambda@disroot.org> | 2025-12-13 21:00:08 -0500 |
| commit | 00dffa8cf5cac8322cb47f2222e424d3960e7939 (patch) | |
| tree | 7db0460694037b063f97058ecb9eb664b6162b45 | |
| parent | de8d06726cae205ead43f8b1ac07ecc59a07363b (diff) | |
| download | cgit-docker-00dffa8cf5cac8322cb47f2222e424d3960e7939.tar.gz cgit-docker-00dffa8cf5cac8322cb47f2222e424d3960e7939.tar.bz2 cgit-docker-00dffa8cf5cac8322cb47f2222e424d3960e7939.zip | |
Added Git Smart HTTP Support
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Dockerfile | 61 | ||||
| -rw-r--r-- | README.md | 16 | ||||
| -rw-r--r-- | cgit.conf | 2 | ||||
| -rw-r--r-- | compose.yml | 13 | ||||
| -rw-r--r-- | entrypoint.sh | 95 | ||||
| -rw-r--r-- | etc/cgitrc | 14 | ||||
| -rw-r--r-- | etc/httpd/conf.d/git-http-apcf.conf | 27 | ||||
| -rw-r--r-- | etc/httpd/conf.d/git-http-cf.conf | 26 | ||||
| -rw-r--r-- | etc/httpd/conf.d/git-http-p.conf | 35 | ||||
| -rw-r--r-- | etc/httpd/conf.d/git-http-pcf.conf | 42 | ||||
| -rw-r--r-- | etc/httpd/conf/httpd.conf | 76 |
12 files changed, 357 insertions, 52 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a7daae --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Don't include the test files +srv/*
\ No newline at end of file @@ -1,35 +1,64 @@ # # -# cgit Docker Container - -FROM rockylinux:9 -LABEL MAINTAINER="RATDAD <lambda@disroot.org>" +# cgit-http-server Docker Container +################### +# Build Stage +################### +FROM rockylinux:9 AS builder # Update everything; install dependencies. -RUN dnf -y update && dnf -y upgrade \ - && dnf install -y git gcc make openssl-devel zlib-devel zip \ - highlight httpd pip \ +RUN dnf -y update && dnf -y install \ + git gcc make \ + openssl-devel zlib-devel zip \ + highlight \ && dnf clean all -# Install cgit. -RUN git clone https://git.zx2c4.com/cgit -ADD cgit.conf cgit -RUN cd cgit \ - && git submodule init \ +# Build cgit. +RUN git clone https://git.zx2c4.com/cgit /build/cgit +WORKDIR /build/cgit +# Add compile-time config (cgit.conf). +ADD cgit.conf . +RUN git submodule init \ && git submodule update \ && make NO_LUA=1 \ - && make install \ - && cd .. \ - && rm -rf cgit + && make install DESTDIR=/build/install + +################### +# Runtime Stage +################### +FROM rockylinux:9 +LABEL MAINTAINER="RATDAD <lambda@disroot.org>" -# Configure. +# Runtime dependencies +RUN dnf -y update && dnf -y install \ + httpd git highlight \ + openssl zlib zip \ + && dnf clean all + +# Install cgit artifacts. +COPY --from=builder /build/install / + +# If set to 0, the container will not \ +# handle git-http-backend for you. +ENV GIT_HTTP_MODE=0 + +# Configure Apache and cgit. ADD etc/cgitrc /etc/cgitrc ADD etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf +# Configure Git HTTP Modes. +ADD etc/httpd/conf.d/git-http-p.conf /etc/httpd/conf.d/git-http-p.conf +ADD etc/httpd/conf.d/git-http-cf.conf /etc/httpd/conf.d/git-http-cf.conf +ADD etc/httpd/conf.d/git-http-pcf.conf /etc/httpd/conf.d/git-http-pcf.conf +ADD etc/httpd/conf.d/git-http-apcf.conf /etc/httpd/conf.d/git-http-apcf.conf + # Add helper scripts. COPY opt/ /opt RUN chmod +x /opt/* +# Prevent git-http-backend safe.directory errors. +RUN git config --system --add safe.directory /srv/git + # Entrypoint. COPY ./entrypoint.sh / RUN chmod +x /entrypoint.sh @@ -13,6 +13,21 @@ You can optionally run with HTTP Basic Auth with these options. docker run --name cgit -d -p 80:80 -v git/repo/location:/srv/git -e HTTP_AUTH_PASSWORD=pass HTTP_AUTH_USER=user ``` +## Docker Build +```bash +# Push-only +docker build --build-arg GIT_HTTP_MODE=w -t cgit:push-only . + +# Full RW +docker build --build-arg GIT_HTTP_MODE=rw -t cgit:rw . + +# Read-only +docker build --build-arg GIT_HTTP_MODE=ro -t cgit:ro . + +# No Git HTTP at all +docker build --build-arg GIT_HTTP_MODE=off -t cgit:no-http . +``` + ## Docker Compose You can use Docker Compose to create an instance of the server. ```yaml @@ -53,4 +68,3 @@ This container runs Apache Web Server. I made this decision because it's one of **To configure**, just mount your custom `httpd.conf` to `/etc/httpd/conf/httpd.conf` inside the container. Just keep in mind that cgit is compiled to serve its files in `/srv/www/htdocs/cgit/` and not the default location of `/var/www/htdocs/cgit/` as the documentation states. This is a decision I made deliberately because `/srv/` is where server files should go. - @@ -4,5 +4,5 @@ # This will be included by the Makefile upon compilation. # Use the standard dir for serving web content. -CGIT_SCRIPT_PATH = /srv/www/htdocs/cgit/ CGIT_CONFIG = /etc/cgitrc +CGIT_SCRIPT_PATH = /srv/www/htdocs/cgit
\ No newline at end of file diff --git a/compose.yml b/compose.yml index 4eea970..82b5ce8 100644 --- a/compose.yml +++ b/compose.yml @@ -3,12 +3,14 @@ # cgit-docker compose example services: cgit: - build: - context: . - dockerfile: Dockerfile # You can also use the pre-built containers hosted at ghcr and docker. # image: ghcr.io/bigratdad/cgit:latest # image: ratdad/cgit:latest + build: + context: . + dockerfile: Dockerfile + # args: + # - ENABLE_GIT_HTTP=0 env_file: - .env ports: @@ -16,6 +18,5 @@ services: volumes: # - ./favicon.ico:/srv/www/htdocs/cgit/favicon.ico # custom favicon # - ./cgit.css:/srv/www/htdocs/cgit/cgit.css # custom cgit.css - - ./etc/httpd/conf/httpd.conf:/etc/httpd/conf/httpd.conf # You may want to change the httpd config on the server. - - ./etc/cgitrc:/etc/cgitrc # You can (and should) bind your own cgitrc. - - ./srv/git/:/srv/git # Bind the location of your git repos to /srv/git in the container. + - ./etc/cgitrc:/etc/cgitrc # You can (and should) bindmount your own cgitrc. + - ./srv/git/:/srv/git:ro # I do not recommend creating repos from inside the container. Make it read-only.
\ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh index a5eadf1..c00077f 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,4 +1,93 @@ -#!/usr/bin/env bash +#!/bin/bash +set -e -/opt/auth.sh -httpd -DFOREGROUND
\ No newline at end of file +export GIT_HTTP_MODE="${GIT_HTTP_MODE:-0}" # Default: disabled +export GIT_HTTP_AUTH=0 # whether http auth is enabled for git-http-backend +export GIT_HTTP_AUTH_FILE="${GIT_HTTP_AUTH_FILE:-/srv/git/.htpasswd}" +# Explicitly override for single .htpasswd for cgit/git-http-server +# GIT_HTTP_AUTH_FILE=/srv/www/htdocs/cgit/.htpasswd +CONF_DIR="/etc/httpd/conf.d" +ACTIVE_CONF="${CONF_DIR}/git-http.conf" + +# Remove any previously selected config +rm -f "${ACTIVE_CONF}" + +# HTTP BASIC AUTH FOR GIT +# Options are 0=disable +# 1=push only, 2=clone/fetch only, +# 3=push,clone/fetch, 4=push,clone/fetch w/auth +case "${GIT_HTTP_MODE}" in + 0) ;; + 1) + ln -s "${CONF_DIR}/git-http-p.conf" "${ACTIVE_CONF}" + echo "Using [git-http] Using Mode 1: git push (auth)" + GIT_HTTP_AUTH=1 + ;; + 2) + ln -s "${CONF_DIR}/git-http-cf.conf" "${ACTIVE_CONF}" + echo "[git-http] Using Mode 2: git clone/fetch only (no auth)" + ;; + 3) + ln -s "${CONF_DIR}/git-http-pcf.conf" "${ACTIVE_CONF}" + echo "[git-http] Using Mode 3: git push (auth) + git clone/fetch (no auth)" + GIT_HTTP_AUTH=1 + ;; + 4) + ln -s "${CONF_DIR}/git-http-apcf.conf" "${ACTIVE_CONF}" + echo "[git-http] Using Mode 4: git push + git clone/fetch (auth)" + GIT_HTTP_AUTH=1 + ;; + *) + echo "[git-http] ERROR: invalid GIT_HTTP_MODE=${GIT_HTTP_MODE}" >&2 + exit 1 + ;; +esac + +# Set up auth credentials for git-http-backend +if [ "$GIT_HTTP_AUTH" -eq 1 ]; then + if [ -z "$GIT_HTTP_AUTH_USER" ] || [ -z "$GIT_HTTP_AUTH_PASSWORD" ]; then + echo "[git-http] ERROR: Auth Enabled, but GIT_HTTP_AUTH_USER/PASSWORD is missing." >&2 + exit 1 + fi + # If .htpasswd exists already, don't recreate it. + if [ ! -f "$GIT_HTTP_AUTH_FILE" ]; then + htpasswd -c -b "$GIT_HTTP_AUTH_FILE" \ + "$GIT_HTTP_AUTH_USER" "$GIT_HTTP_AUTH_PASSWORD" + echo "[git-http] INFO: Credentials written to ${GIT_HTTP_AUTH_FILE}." + else + htpasswd -b "$GIT_HTTP_AUTH_FILE" \ + "$GIT_HTTP_AUTH_USER" "$GIT_HTTP_AUTH_PASSWORD" + echo "[git-http] INFO: Using ${GIT_HTTP_AUTH_FILE} for auth credentials." + fi + + # Ensure proper permissions for /srv/git/.htpasswd + chown root:apache "$GIT_HTTP_AUTH_FILE" + chmod 640 "$GIT_HTTP_AUTH_FILE" +fi + +# HTTP BASIC AUTH FOR CGIT +if [ -n "$HTTP_AUTH_PASSWORD" ]; then + HTTP_AUTH_USER="${HTTP_AUTH_USER:-admin}" + + # Create a .htaccess file. + cat > /srv/www/htdocs/cgit/.htaccess <<EOF +AuthType Basic +AuthName "CGit" +AuthUserFile /srv/www/htdocs/cgit/.htpasswd +Require valid-user +EOF + + if [ ! -f /srv/www/htdocs/cgit/.htpasswd ]; then + htpasswd -c -b /srv/www/htdocs/cgit/.htpasswd \ + "$HTTP_AUTH_USER" "$HTTP_AUTH_PASSWORD" + else + htpasswd -b /srv/www/htdocs/cgit/.htpasswd \ + "$HTTP_AUTH_USER" "$HTTP_AUTH_PASSWORD" + fi + + # Ensure correct permissions for .htpasswd + chown root:apache /srv/www/htdocs/cgit/.htpasswd + chmod 640 /srv/www/htdocs/cgit/.htpasswd +fi + +exec /usr/sbin/httpd -DFOREGROUND @@ -9,13 +9,20 @@ robots=noindex, nofollow snapshots=tar.gz tar.bz2 zip source-filter=/opt/highlight.sh +# Assets +css=/cgit.css +logo=/cgit.png +favicon=/favicon.ico + # -# Site Settings +# Behavior Settings enable-index-links=1 enable-remote-branches=1 enable-log-filecount=1 enable-log-linecount=1 +enable-http-clone=1 enable-git-config=1 +remove-suffix=1 # # Cache Settings @@ -23,8 +30,7 @@ cache-root=/var/cache/cgit cache-size=1000 # -# List of common mimetypes -# +# Mimetypes mimetype.gif=image/gif mimetype.html=text/html mimetype.jpg=image/jpeg @@ -65,6 +71,6 @@ readme=:install.txt readme=:INSTALL readme=:install -# NOTE: According to the cgit mailing list, these options should go last. +# Don't change. virtual-root=/ scan-path=/srv/git
\ No newline at end of file diff --git a/etc/httpd/conf.d/git-http-apcf.conf b/etc/httpd/conf.d/git-http-apcf.conf new file mode 100644 index 0000000..d781aea --- /dev/null +++ b/etc/httpd/conf.d/git-http-apcf.conf @@ -0,0 +1,27 @@ +# +# +# Git Smart HTTP Support (authenticated read/write) +# git push, clone, fetch — ALL authenticated + +SetEnv GIT_PROJECT_ROOT /srv/git +SetEnv GIT_HTTP_EXPORT_ALL 1 + +# Expose all of the endpoints. +ScriptAliasMatch "^(/.+/(git-upload-pack|git-receive-pack|info/refs))$" \ + /usr/libexec/git-core/git-http-backend$1 + +# Globally authenticate for each endpoint. +<LocationMatch "^/.+/(git-upload-pack|git-receive-pack|info/refs)$"> + AuthType Basic + AuthName "Git Access" + AuthUserFile ${GIT_HTTP_AUTH_FILE} + Require valid-user +</LocationMatch> + +# info/refs strictly scoped +<LocationMatch "^/.+/info/refs$"> + <RequireAny> + Require expr %{QUERY_STRING} == "service=git-upload-pack" + Require expr %{QUERY_STRING} == "service=git-receive-pack" + </RequireAny> +</LocationMatch> diff --git a/etc/httpd/conf.d/git-http-cf.conf b/etc/httpd/conf.d/git-http-cf.conf new file mode 100644 index 0000000..0d4302e --- /dev/null +++ b/etc/httpd/conf.d/git-http-cf.conf @@ -0,0 +1,26 @@ +# +# +# Git Smart HTTP Support (readonly) +# clone/fetch ONLY + +SetEnv GIT_PROJECT_ROOT /srv/git +SetEnv GIT_HTTP_EXPORT_ALL 1 + +# Expose git-upload-pack and info/refs. +ScriptAliasMatch "^(/.+/(git-upload-pack|info/refs))$" \ + /usr/libexec/git-core/git-http-backend$1 + +# No authentication needed for git clone/fetch. +<LocationMatch "^/.+/git-upload-pack$"> + Require all granted +</LocationMatch> + +# Only allow info/refs for git clone/fetch. +<LocationMatch "^/.+/info/refs$"> + Require expr %{QUERY_STRING} == "service=git-upload-pack" +</LocationMatch> + +# Explicitly deny git push just in case. +<LocationMatch "^/.+/git-receive-pack$"> + Require all denied +</LocationMatch> diff --git a/etc/httpd/conf.d/git-http-p.conf b/etc/httpd/conf.d/git-http-p.conf new file mode 100644 index 0000000..b30ad47 --- /dev/null +++ b/etc/httpd/conf.d/git-http-p.conf @@ -0,0 +1,35 @@ +# +# +# Git Smart HTTP Support +# git push ONLY + +SetEnv GIT_PROJECT_ROOT /srv/git +SetEnv GIT_HTTP_EXPORT_ALL 1 + +# Expose git-receive-pack and info/refs. +ScriptAliasMatch "^(/.+/(git-receive-pack|info/refs))$" \ + /usr/libexec/git-core/git-http-backend$1 + +# Authenticate against git push. +<LocationMatch "^/.+/(git-receive-pack)$"> + AuthType Basic + AuthName "Git Push Access" + AuthUserFile ${GIT_HTTP_AUTH_FILE} + Require valid-user +</LocationMatch> + +# Deny info/refs push unless it's for git push. +<LocationMatch "^/.+/info/refs$"> + AuthType Basic + AuthName "Git Push Access" + AuthUserFile ${GIT_HTTP_AUTH_FILE} + <RequireAll> + Require expr %{QUERY_STRING} == "service=git-receive-pack" + Require valid-user + </RequireAll> +</LocationMatch> + +# Explicitly deny git clone/fetch just in case. +<LocationMatch "^/.+/git-upload-pack$"> + Require all denied +</LocationMatch> diff --git a/etc/httpd/conf.d/git-http-pcf.conf b/etc/httpd/conf.d/git-http-pcf.conf new file mode 100644 index 0000000..d343443 --- /dev/null +++ b/etc/httpd/conf.d/git-http-pcf.conf @@ -0,0 +1,42 @@ +# +# +# Git Smart HTTP Support (read/write) +# git push, clone and fetch allowed + +SetEnv GIT_PROJECT_ROOT /srv/git +SetEnv GIT_HTTP_EXPORT_ALL 1 + +# Expose git-upload/receive-pack and info/refs. +ScriptAliasMatch "^(/.+/(git-upload-pack|git-receive-pack|info/refs))$" \ + /usr/libexec/git-core/git-http-backend$1 + +# Authenticate against git push. +<LocationMatch "^/.+/git-receive-pack$"> + AuthType Basic + AuthName "Git Push Access" + AuthUserFile ${GIT_HTTP_AUTH_FILE} + Require valid-user +</LocationMatch> + +# Only allow git-upload-pack or git-receive-pack services and nothing else. +<LocationMatch "^/.+/info/refs$"> + AuthType Basic + AuthName "Git Push Access" + AuthUserFile ${GIT_HTTP_AUTH_FILE} + + <RequireAny> + # git clone/fetch, no auth + Require expr %{QUERY_STRING} == "service=git-upload-pack" + + # git push, authenticated + <RequireAll> + Require expr %{QUERY_STRING} == "service=git-receive-pack" + Require valid-user + </RequireAll> + </RequireAny> +</LocationMatch> + +# Allow git clone/fetch w/o auth. +<LocationMatch "^/.+/git-upload-pack$"> + Require all granted +</LocationMatch>
\ No newline at end of file diff --git a/etc/httpd/conf/httpd.conf b/etc/httpd/conf/httpd.conf index 3b6da71..675241d 100644 --- a/etc/httpd/conf/httpd.conf +++ b/etc/httpd/conf/httpd.conf @@ -16,7 +16,14 @@ LoadModule dir_module modules/mod_dir.so LoadModule rewrite_module modules/mod_rewrite.so LoadModule alias_module modules/mod_alias.so LoadModule mpm_prefork_module modules/mod_mpm_prefork.so - +LoadModule env_module modules/mod_env.so +LoadModule headers_module modules/mod_headers.so +LoadModule expires_module modules/mod_expires.so +# And Basic Auth Modules +LoadModule auth_basic_module modules/mod_auth_basic.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authn_file_module modules/mod_authn_file.so +LoadModule authz_user_module modules/mod_authz_user.so # Load CGI Module <IfModule !mpm_prefork_module> LoadModule cgid_module modules/mod_cgid.so @@ -25,41 +32,68 @@ LoadModule mpm_prefork_module modules/mod_mpm_prefork.so LoadModule cgi_module modules/mod_cgi.so </IfModule> -# And Basic Auth Modules -LoadModule auth_basic_module modules/mod_auth_basic.so -LoadModule authn_core_module modules/mod_authn_core.so -LoadModule authn_file_module modules/mod_authn_file.so -LoadModule authz_user_module modules/mod_authz_user.so - # # Server config -Listen 0.0.0.0:80 +Listen 80 ServerName localhost -ServerAdmin root@localhost EnableSendFile on AddDefaultCharset UTF-8 TypesConfig /etc/mime.types MIMEMagicFile conf/magic AddHandler cgi-script .cgi +# +# Log Config +LogLevel warn +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%h %l %u %t \"%r\" %>s %b" common +ErrorLog "logs/error_log" +ScriptLog logs/git-http-debug.log +CustomLog "logs/access_log" combined + +# +# Git Smart HTTP Support (if enabled) +PassEnv GIT_HTTP_AUTH_FILE +IncludeOptional conf.d/git-http.conf + +# # Always wear protection. <Directory /> - Require all denied + Require all granted </Directory> -# NOTE: Alias matcher MUST end in /, not /cgit.cgi. It WILL break otherwise. -# ALSO: "cgitrc must have a virtual-root=/". -# Remove /cgit.cgi/ from url paths. -ScriptAlias "/" "/srv/www/htdocs/cgit/cgit.cgi/" -<Directory "/srv/www/htdocs/cgit/"> - DirectoryIndex cgit.cgi - AllowOverride None - Options +ExecCGI +FollowSymLinks - SetHandler cgi-script +# +# ALSO: cgitrc must have this: virtual-root=/ +DocumentRoot "/srv/www/htdocs/cgit" +<Directory "/srv/www/htdocs/cgit"> Require all granted + # -Indexes here is not strictly necessary; + # Added for good hygiene + Options +ExecCGI -Indexes + DirectoryIndex cgit.cgi + AllowOverride All + + RewriteEngine On + + # Hard stop: never rewrite Git HTTP requests. + RewriteRule ^.+/(git-upload-pack|git-receive-pack|info/refs)$ - [END] + + # Serve static files directly. + RewriteCond %{REQUEST_FILENAME} -f + RewriteRule ^ - [END] + + # Let cgit handle everything else (and stay off my url). + RewriteRule ^(.*)$ cgit.cgi/$1 [END] + + # Cache static assets + ExpiresActive On + <FilesMatch "\.(css|js|png|ico)$"> + ExpiresDefault "access plus 30 days" + Header set Cache-Control "public, max-age=2592000, immutable" + </FilesMatch> </Directory> -# Deny access to .htaccess/.htpasswd +# Deny access to .htaccess/.htpasswd. <Files ".ht"> Require all denied -</Files>
\ No newline at end of file +</Files> |
