Control git flow
Switch branches/tags
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.

GitHandler Build Status

Accept and handle git repository requests over SSH


Install using rubygems:

gem install git_handler

Or using latest source code:

git clone git://
cd git-handler
bundle install
rake install


If you already have an operation system configured, make sure you have git user in your system. In order to use git_handler you'll need to generate a customized SSH public key and add it to ~/.ssh/authorized_keys on server. Generation should be something that needs to be implemented in your application or script, there is functionality already built for that:

require 'git_handler/public_key'

# Load your current pub key
content ='~/.ssh/'))

# Create a key
key =

Now, to convert loaded key into a system key just run:

# => command="/usr/bin/git_proxy",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNjN3ZUOoosWeuJ7KczE5FAOzwZ+Z51KSQvqTCb7ccBi4u+pPYcGEYr2t0cx/BUcx/ZGE8ih+zxN1qM8KmM0uluuy54itHsKFdAwoibkbG22fQc2DY0RmktXXB/w6LxmFuQrmz0fkcbkE39pm5k6Nw6mqks5HjM7aDXRdwM8fSrq0PjfUNiESIrIAeEMGhtZFaj+WZVMfXaIlgzxZsAUpUULhN4j069v8VgxWyyOUT+gwcQB8lVc0BVYhptlFaJBtwhfWvOAviSuK7Cpjh60NdkZ3R2QYeh6wb6fF+KGCkM4iED4PZ1Ep8fRzrbCHky4VHSOyOvg9qKcgP1h+e+diD 

SSH public key is now ready for usage on server side. Drop it into ~/home/git/.ssh/authorized_keys file if your user is git. The whole purpose of key modifications is that we're restricting SSH to a specific command or script on server, which gives us ability to control permissions and other restrictions.

Control script

In the example above as you can see we specify /usr/bin/git_proxy to be executed once SSH connection is being established. GitHandler provides a simple api to verify and execute git request that comes from client.

Example of /usr/bin/git_proxy file:

#!/usr/bin/env ruby
require 'git_handler'

config =

# Configuration has a bunch of options:
# :user       - Git user, default: git
# :home_path  - Home path, default: /home/git
# :repos_path - Path to repositories, default: /home/git/repositories
# :log_path   - Git requests logger, default: /var/log/git_handler.log

  session =
  session.execute(ARGV, ENV)
rescue Exception => ex
  STDERR.puts "Error: #{ex.message}"

NOTE: Script must have permissions for execution.

Session instance will check if incoming git request has a valid environment and valid git command. After check is complete it will shell out to git-shell -c COMMAND to perform an original git command. Providing block to session.execute will override default and allow you to control the logic:

session.execute(ARGV, ENV) do |request|

  # Yields GitHandler::Request instance that
  # contains all information about git request, env and repo

  STDERR.puts "-----------------------------"
  STDERR.puts "REMOTE IP: #{request.remote_ip}"
  STDERR.puts "ARGS: #{request.args.inspect}"
  STDERR.puts "ENV: #{request.env.inspect}"
  STDERR.puts "REPO: #{request.repo}"
  STDERR.puts "REPO PATH: #{request.repo_path}"
  STDERR.puts "COMMAND: #{request.command}"
  STDERR.puts "-----------------------------"

By default, if request has invalid environment attributes or not a git request, session raises GitHandler::SessionError. If you dont want to handle exceptions, just use session.execute_safe method:

session =
session.execute_safe(ARGV, ENV)

To test if all that works try this:

ssh -vT

In the debug output you'll something similar:

debug1: Remote: Agent forwarding disabled.
debug1: Remote: Pty allocation disabled.
debug1: Remote: Forced command.
debug1: Remote: Port forwarding disabled.
debug1: Remote: X11 forwarding disabled.
debug1: Remote: Agent forwarding disabled.
debug1: Remote: Pty allocation disabled.
debug1: Sending environment.
debug1: Sending env LANG = en_US.UTF-8

>>> Error: Invalid git request <<<<

debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug1: client_input_channel_req: channel 0 rtype reply 0
debug1: channel 0: free: client-session, nchannels 1
Transferred: sent 2384, received 2880 bytes, in 0.3 seconds
Bytes per second: sent 7308.1, received 8828.6
debug1: Exit status 1

This means that everything works. Script does not provide any shell access and only allows git requests. To test that, create an empty repository:

mkdir /home/git/repositories
cd /home/git/repositories
git init --bare testrepo.git

And clone it (on local machine):

git clone

Server side configuration

In case you dont have a git user on your server, here is a quick manual on how to get it rolling.

Create a git user:

adduser --home /home/git --disabled-password git

Restrict SSH authentication only via public keys. Open file /etc/ssh/sshd_config and add this snippet to the end:

Match User !root
  PasswordAuthentication no

This will disable password authentications for everyone except root, or other user of your choice. You'll need to restart ssh daemon:

/etc/init.d/ssh restart

Authorized Keys

GitHandler provides a simple api to manage your authorized_keys file content.

Each write operation issues a lock File::LOCK_EX on file.


require 'git_handler/public_key'
require 'git_handler/authorized_keys'

# Read your local ssh public key content
content ='~/.ssh/'))

# Create a new key
key =

# Write formatted key to authorized_keys file
GitHandler::AuthorizedKeys.write_key('/path/to/file', key, 'my_command')

You can also write multiple keys:

GitHandler::AuthorizedKeys.write_keys('/path/to/file', [k1, k2, k3], 'my_command')


To run the test suite execute:

rake test


See LICENSE file for details