Skip to content

Git Server

Introduction

This describes on how to install a plain Git server.

Installation

Create User and it's Home Directory

As root user execute the following.

# Create a system user for git.
useradd --system git
# No login for this git by locking it and set a git only shell.
usermod -L git --home /srv/git --shell /usr/bin/git-shell.sh
# Create home and storage directory for git.
mkdir /srv/git /srv/git/.ssh
# Set the owner ship.
chown git:git /srv/git /srv/git/.ssh
# Set the access mode on the .ssh directory.
chmod a-rwx,u+rwx /srv/git/.ssh

Create an empty ssh authorization file having the correct access.

# Create an empty authorized ssh keys file. 
touch /srv/git/.ssh/authorized_keys
chown git:git /srv/git/.ssh/authorized_keys
chmod a-rwx,u+rw /srv/git/.ssh/authorized_keys

Add your key of the forwarded ssh-agent when used to the ssh authorization file.

ssh-add -L >> /srv/git/.ssh/authorized_keys

Create an empty Initialized Repository

su - git -c 'mkdir /srv/git/repos/shared/test.git && git -C /srv/git/repos/shared/test.git init --bare`

Limit the Git users Access

To limit the git user a custom shell is configured.

usermod --shell /usr/local/bin/git-shell.sh git
The shell script git-shell.sh itself is as follows.

#!/bin/bash

# Get the script directory.
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

# Writes to stderr.
#
function WriteLog()
{
  echo "$@" 1>&2;
  logger -- "$@"
}

function RepoBranchesInfo()
{
# --format='%(HEAD) %(color:yellow)%(refname:short)%(color:reset) - %(color:red)%(objectname:short)%(color:reset)\
# - %(contents:subject) - %(authorname) (%(color:green)%(committerdate:relative)%(color:reset))'
  echo "$(echo "$1"| sed 's/\.\/repos\//  /g') $(du --summarize --human "$1" | awk '{print $1}') $(git -C "$1" tag)"
  git -C "$1" for-each-ref \
    --sort=-committerdate refs/heads/ \
    --format='     %(HEAD) %(refname:short) %(objectname:short) "%(authorname)" [%(committerdate:relative)] %(contents:subject)'
  echo ""

}
export -f RepoBranchesInfo

# Log the requested command string.
logger "Requested command: $*"

# Get the current working directory.
root_dir="$(pwd)"

if [[ $# -ne 2 ]] || [[ "$1" != "-c" ]] ; then
  WriteLog "Interactive login not permitted!"
  exit 1
fi

# Shift the arguments.
set -- $2

case "$1" in
  git-upload-pack|git-receive-pack)
    if [[ $# != 2 ]] ; then
      WriteLog "Wrong number '$#' of arguments!"
      exit 1
    fi
    ;;

  list)
      du --one-file-system --dereference-args --si --max-depth 2 repos | grep '\.git$' | sed 's/repos\///g'
    exit 0
    ;;

  show)
      echo -e "Repositories with current branch information\n"
      find . -regex '^.*\.git$' -exec bash -c 'RepoBranchesInfo "{}"' \;
    exit 0
    ;;

  create)
    if [[ $# != 2 ]] ; then
      WriteLog "Wrong number '$#' of arguments!"
      exit 1
    fi
    pattern='^[0-9a-zA-Z\-]*/[0-9a-zA-Z\-]*\.git$'
    # Check repo group name for unwanted chars.
    if [[ ! $2 =~ $pattern ]] ; then
      WriteLog "Repository path '$2' not in the format of '<group>/<name>.git' !"
      exit 1
    fi
    ;;

  help | *)
    if [[ "$1" != "help" ]] ; then 
      WriteLog "Invalid command '$1'!" 
    fi
      WriteLog " Valid commands are:
  create <group>/<name>.git: 
    Creates a raw initialized and empty repository.
  list:
    Lists all repositories on the server under this account.
  show:
    Shows all repositories on the server under this account including all their branches.
  git-upload-pack, git-receive-pack:
    Commands used by local git aqpplication to communicate to remote.
"
    exit 1
    ;;
esac

# Trim the single quotes.
repo_dir="$(echo "$2" | tr -d "\'")"

if [[ -z "${repo_dir}" ]] ; then
  WriteLog "No repository passed!"
  exit 1
fi


# When the command to create a bare repo 
if [[ "$1" == 'create' ]] ; then
  # Assemble full directory path.
  abs_dir="${root_dir}/repos/${repo_dir}"
  # Check if the new path already exists.
  if [[ -d "${abs_dir}" ]] ; then
    WriteLog "Git repository '${abs_dir}' already exists!"
    # Bailout.
    exit 1
  fi
  # Create the repository directory path.
  if ! mkdir -p "${abs_dir}" ; then
    WriteLog "Repository directory '${abs_dir}' failed to create!"
    exit 1
  fi
  # Initialize the bare repository.
  if git -C "${abs_dir}" init --bare ; then
    WriteLog "Git repository '$2' initialized'."
    exit 0
  fi  
  WriteLog "Git creation failed '${abs_dir}'!"
  exit 1
fi 

# Assemble fule path
abs_dir="$(realpath "${root_dir}/repos/${repo_dir}")"
# Check if the git directory exists.
if [[ ! -d "${abs_dir}" ]] ; then
  WriteLog "Access denied ($?) for $2 ($abs_dir)!"
  exit 1
fi

# Log the transformed command.
logger "Translated command: $1 '$abs_dir'"
# Perform the transformed command.
"$1" "${abs_dir}"

Shell Script SSH Commands

The shell script introduces commands that are able to be called using ssh git@<domain> <command>.

To get the list of commands use help like:

ssh git@<domain> help