GitHandler Build Status

A tool to simplify your git flow customizations. Its main purpose is to provide an application-based control layer for Git request processing.

Installation

Install using rubygems:

gem install git_handler

Or using latest source code:

git clone git://github.com/sosedoff/git-handler.git
cd git-handler
bundle install
rake install

Usage

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 = File.read(File.expand_path('~/.ssh/id_rsa.pub'))

# Create a key
key = GitHandler::PublicKey.new(content)

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

key.to_system_key('/usr/bin/git_proxy')
# => 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 = GitHandler::Configuration.new

# 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

begin
  session = GitHandler::Session.new(config)
  session.execute(ARGV, ENV)
rescue Exception => ex
  STDERR.puts "Error: #{ex.message}"
  exit(1)
end

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 "-----------------------------"
end

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 = GitHandler::Session.new(config)
session.execute_safe(ARGV, ENV)

To test if all that works try this:

ssh -vT git@YOUR_HOST.com

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 [email protected] 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 git@YOUR_HOST.com:testrepo.git

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.

Example:

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

# Read your local ssh public key content
content = File.read(File.expand_path('~/.ssh/id_rsa.pub'))

# Create a new key
key = GitHandler::PublicKey.new(content)

# 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')

Testing

To run the test suite execute:

rake test

License

Copyright (c) 2012 Dan Sosedoff.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.