Install GIT as public repository on Debian

From Wikichris
Jump to: navigation, search

In this tutorial, we want to install GIT on a debian server, once installed coworkers must be allowed to access our GIT repositories via Apache or SSH. The Coworkers can be on Mac OS X, Linux or Windows.

I tested every written lines of this page with:

  • Debian 6 (Squeeze) as a Server and a Client
  • Mac OS 10.6 (Snow Leopard) as a Client
  • Windows 7 as a Client

Install locally, on the Server

Git cannot create the remote repository, it only operates on existing ones, so we need to create an empty repository locally on the server.

GIT itself

As you certainly guessed, we start by installing Git with Aptitude

apt-get install git

For each commit we make while using GIT, a name and email are necessary, let's introduce ourself

git config --global user.name "chris"
git config --global user.email chris@youremail.com

To check if the user as been recorded

git config -l

It will show

user.name=chris
user.email=chris@youremail.com

A first project

Let's create a first project (test001)

cd /var/cache/git
mkdir test001
cd test001

NB: you must not name your folder test001.git, otherwise the apache config won't work. Name it test001 only For the moment it's a normal directory, let's make it a GIT directory :

git --bare init




NB: if we didn't want to use it remotely we could have created a simple local repository with the following command

cd /path/myfolder
git init

And later we could still create a copy, ready for a remote copy into the server folder /var/cache/git

git --bare clone /path/myfolder /path/newfolder

The new folder would be usable only as remote repository in order to clone from it, and then pull and push, not as a working repository dedicated to the commit.




Now let's create a working folder in my home folder

mkdir ~/projects/
cd ~/projects
git clone /var/cache/git/test001 test001.git

Let's change the project description in this file : ~/projects/test001.git/.git/description

My first GIT project - Hello World

Let's exclude some annoying files from the commits, add in this file: ~/projects/test001.git/.gitignore

cd ~/projects/test001.git
echo .DS_Store >> .gitignore
echo Thumb.db >>.gitignore
git add .gitignore
git commit -a -m "gitignore configured"




NB: it's annoying, but .git/info/exclude and description can be changed only locally and will never be sent to or recovered from the server.

NB2: .DS_Store on Mac, Thumb.db on Windows, keep thumbnails of your photos folders and should never be backed up.




Now the working folder is ready, let's create the first file ~/projects/test001.git/test001.php

<?php
echo "hello world!";
?>

For now, GIT still does not recognize the file test001.php. We need to explicitly add the file to tell GIT to start tracking it

cd ~/projects/test001.git
git add test001.php

Status will show you that the file is still not commited

git status
git commit -a -m "My very first commit"

(-a is for ALL FILES, -m for Message associated to this commit)

To see history

git log


NB: when we cloned the repository, git stored its location in the repository configuration, and that location is used for pulls. But you can change it in ~/projects/test001/.git/config

To push your commit to the server this simple command will do it

git push origin master

NB: if you are not doing this test with root, you won't be allowed to push yet, the security is setup later on this page

And if you want to download the changes made by somebody else

git pull

In conclusion for this section, the folder ~/projects/test001 allowed us to verify that everything works perfectly locally, without SSH or HTTP connexion. If your PUSH and PULL works properly here, you can continue to the next section.

Share it with Apache (HTTP)

While we will allow the GIT clients to access our repository via HTTP(S), we will also install GITWEB to get a Web Interface directly browsable.

Install of the Web Interface

if Apache is not installed yet

apt-get install apache2

we need to install some basic files which will enable us to publish our repositories on Apache

apt-get install gitweb

It creates the following folder

cd /usr/share/gitweb

The location for publishable repository is defined by the variable $projectroot from /etc/gitweb.conf, which happens to be

/var/cache/git

With a standard Apache install, because of the file /etc/apache2/conf.d/gitweb my repository is now viewable at

http://server.domaine.com/gitweb/

On an existant Apache, this config is an issue, so I edit this file and comment the first line

#Alias /gitweb /usr/share/gitweb

<Directory /usr/share/gitweb>
 Options FollowSymLinks +ExecCGI
 AddHandler cgi-script .cgi
</Directory>

Let's reload apache

/etc/init.d/apache2 reload

Activate Read/Write Access

Before early 2010, WebDAV was the only solution in order to commit to the server via HTTP. It was a very slow process. Now WebDAV is useless, the SMART HTTP method allows the use of POST, with only one file containing everything (much faster)

We will use the rewriting capabilities of Apache

a2enmod rewrite

But if you want to allow your client to PUSH (their COMMIT) into your repositories, you will have to change the folder access in order to allow www-data to write

chown -R root.www-data /var/cache/git/test001
chmod -R g+w /var/cache/git/test001

Create /etc/apache2/sites-available/git for http://git.company.com

#The following commented lines are for a HTTPS server instead of unsecured HTTP
#<IfModule mod_ssl.c>
#<VirtualHost _default_:443>
   #SSLEngine on
   #SSLCertificateFile    /etc/ssl/private/server.crt
   #SSLCertificateKeyFile /etc/ssl/private/server.key
   #SSLCertificateChainFile /etc/ssl/private/bundle.crt
   #if you uncomment the previous lines, also uncomment the last line of the file and comment the following one:
<VirtualHost _default_:80>
   SetEnv GITWEB_CONFIG /etc/gitweb.conf
   SetEnv GIT_PROJECT_ROOT /var/cache/git
   SetEnv GIT_HTTP_EXPORT_ALL
   ServerName git.company.com
   DocumentRoot /usr/share/gitweb
   AliasMatch ^/(.*/objects/[0-9a-f]{2}/[0-9a-f]{38})$          /var/cache/git/$1
   AliasMatch ^/(.*/objects/pack/pack-[0-9a-f]{40}.(pack|idx))$ /var/cache/git/$1
   ScriptAliasMatch \
   		"(?x)^/(.*?)\.git/(HEAD | \
   						info/refs | \
   						objects/info/[^/]+ | \
   						git-(upload|receive)-pack)$" \
   		/usr/lib/git-core/git-http-backend/$1/$2
   #ScriptAlias / /usr/share/gitweb/

   <LocationMatch "^/.*?\.git/.*?$">
      AuthType Basic
      AuthName "git repository"
      AuthUserFile /var/cache/git/htpasswd.git
      AuthGroupFile /var/cache/git/htgroup.git
      Require group cloners
   </LocationMatch>

   <LocationMatch "^/.*?\.git/git-receive-pack$">
      AuthType Basic
      AuthName "git repository"
      AuthUserFile /var/cache/git/htpasswd.git
      AuthGroupFile /var/cache/git/htgroup.git
      Require group commiters
   </LocationMatch>

   <LocationMatch "^/test001\.git/.*?$">
      AuthType Basic
      AuthName "git repository"
      AuthUserFile /var/cache/git/htpasswd.git
      AuthGroupFile /var/cache/git/htgroup.git
      Require group test001-read
   </LocationMatch>

   <LocationMatch "^/test001\.git/git-receive-pack$">
      AuthType Basic
      AuthName "git repository"
      AuthUserFile /var/cache/git/htpasswd.git
      AuthGroupFile /var/cache/git/htgroup.git
      Require group test001-write
   </LocationMatch>

  <Directory /usr/share/gitweb/>
       Options ExecCGI FollowSymLinks Indexes
       Allow from all
       Order allow,deny
       AddHandler cgi-script cgi
       DirectoryIndex index.cgi
       RewriteEngine on
       RewriteCond %{REQUEST_FILENAME} !-f
       RewriteRule ^.* /index.cgi/$0 [L,PT]
  </Directory>

</VirtualHost>
#</IfModule>

And enable this site

cd /etc/apache2/sites-enabled/
ln -s ../sites-available/git 99-git

Restart apache

/etc/init.d/apache2 restart

Create the password file with a first user (chris) and choose a password for him

htpasswd -c /var/cache/git/htpasswd.git chris

If you want to add a second user

htpasswd /var/cache/git/htpasswd.git user2

Then create the following groups by creating the file /var/cache/git/htgroup.git

vi /var/cache/git/htgroup.git

test001-read:chris user2 user3
test001-write:chris user2
commiters:chris
cloners:chris user2 user5

NB: cloners/commiters will have access to every repository except test001 which have its own groups test001-read/test001-write

NB2: each write access must be allowed in couple with a read access (Write access does not work without read access)

The folder must be writable by apache. An easy way to keep writing rights for CHRIS and Apache is:

chown -R chris.www-data /var/cache/git/test001
chmod -R 770 /var/cache/git/test001

It should work, from now you have:

HTTP Access from clients on Linux or Mac OS X

If you need to commit, define username and email

git config --global user.name "chris"
git config --global user.email chris@youremail.com

To clone the Repository on your computer

git clone http://git.company.com/test001.git/

Or you can avoid to type your login each time

git clone http://chris@git.company.com/test001.git/

Even your password if you really want, during your tests

git clone http://chris:mypasswd@git.company.com/test001.git/

NB: the login used in HTTP is not linked to the one used by "git config --global user.name "chris""

Each user should locally create the file ~/.gitconfig with the following content

[user]
    name = chris
    email = chris@youremail.com

[core]
    excludesfile = /Users/chris/.gitignore

[http]
    #sslCAInfo=/Users/chris/.git.company.com.cer
    #sslVerify=no

And add .DS_Store and Thumb.db to the ignore list

echo ".DS_Store" >> ~/.gitignore
echo "Thumb.db" >> ~/.gitignore

NB: for using HTTPS with a self-signed certificate, uncomment sslCAInfo which lets you target a PEM certificate. I exported it from Firefox

  1. Accept the certificat in firefox
  2. Export in PEM format in “Preferences”->”Advanced”->”View Certificates”
  3. name the file ~/.git.company.com.cer

Or you can uncomment sslVeriry which will avoid to check the certificate

HTTP Access from clients on Windows

If you like the command line, you can install only GIT for Windows and follow the Linux/Mac previous bloc, but I doubt that most Windows user would enjoy, TortoiseGit seems the best graphical choice for them.

TortoiseGit works with HTTP and SSH. So we must consider a proper installation of SSH in the same time, even if you are not sure to use it one day.

Install Git for Windows (msysgit) and then TortoiseGit

If you don't have Putty, download its suite first to enable the Use of SSH

Their install is very easy, when installing msysgit, I chose:

  • Run Git from the Windows Command Prompt
  • Use (Tortoise)Plink (not OpenSSH)
  • my Plink was in c:\program files\putty\plink.exe (I installed Putty and all its tools months ago)
  • Checkout Windows-style

After that, idem for tortoisegit: I didn't chose OpenSSH either

Now TortoiseGit is as easy as TortoiseSVN

Anyway, I could not make it work with SSH, while for HTTP is worked fine :)

Share it with SSH

HTTP seems easier, but SSH can be a good choice in some case. Personnaly I would use SSH for me and HTTP for external contractor since I can avoid to create a user account on linux.

I assume that things are set up so that you can ssh to your server without having to type a password, which means most of the time that :

  • your public key is on the server in ~/.ssh/authorized_keys
  • you are running ssh-agent locally (already started on Mac OS X)
  • you use locally ~/.ssh/config as following if you have a special port (22222) and a different login (chris)

an example of ~/.ssh/config could be the following

Host *.company.com
  port 22222
  protocol 2
  PubKeyAuthentication yes
  PasswordAuthentication no
  User chris

Lets remember that the location of the repository is:

/var/cache/git/test001/

And the Server name is

git.company.com

This user (here Chris) must have read access on this folder, but it's certainly already like that. Otherwise the next section is about Security.

SSH clients on Mac OS X or Linux

NB: git is automatically installed by XCode on Mac OS X

just use the following command on Mac or Linux

git clone ssh://git.company.com/var/cache/git/test001

Then, after some

git commit -a -m "blablabla"

you can push the update to the server with

git push

or the first time with

git push origin master

It's as easy as that with ssh on Linux/Mac OS X.

SSH Access from clients on Windows

The tools used for SSH are the same than HTTP, look at this previous paragraph:

Anyway, I could not make it work with SSH, meanwhile with HTTP is worked fine.

Security

HTTP

We already configured Apache to take care of the security

the users must be added in /var/cache/git/htpasswd.git

htpasswd /var/cache/git/htpasswd.git user4

the groups are managed in /var/cache/git/htgroup.git

essai001-read:user1 user4 user2
essai001-write:user1 user4
project002-read:user3 user4 user1 user2
project003-write:user3 user4

NB: adding a user or a group can be done without restarting Apache

Every new repository will be shared via HTTP to the groups commiters and cloners, if you want to replace this groups you must add in /etc/apache2/sites-available/git

   <LocationMatch "^/project002\.git/.*?$">
      AuthType Basic
      AuthName "git repository"
      AuthUserFile /var/cache/git/htpasswd.git
      AuthGroupFile /var/cache/git/htgroup.git
      Require group project002-read
   </LocationMatch>

   <LocationMatch "^/project002\.git/git-receive-pack$">
      AuthType Basic
      AuthName "git repository"
      AuthUserFile /var/cache/git/htpasswd.git
      AuthGroupFile /var/cache/git/htgroup.git
      Require group project002-write
   </LocationMatch>

And reload apache

/etc/init.d/apache2 reload

www-data must be allowed to access to this repository

chown -R chris.www-data /var/cache/git/project002
chmod -R 770 /var/cache/git/project002

If you don't allow www-data to access to the repository, it won't be listed in gitweb either.

SSH

One of the nice design decisions made by the Git developers is that access control should not be the responsibility of the SCM tool. This means that the tool is free to concentrate on doing its job (tracking content), while leaving questions of authentication and authorization in the hands of other tools which are much more flexible and better suited to the job: tools like SSH, filesystem permissions, ACLs, and a host of other mechanisms.

Let's see 2 possibilities

Simple access control

We can define 2 kinds of users :

  • public: read only on open source project
  • developer: read/write on every project

In this case we can control the access with SSH Let's create a user GIT

adduser git

And give him all the ownership of the repositories

chmod -R git /var/cache/git

Or for staying apache capable

chmod -R git.www-data /var/cache/git

Add each developer's public key in /home/git/.ssh/authorized_keys

I didn't test this method, but its simplicity seems very interesting. If all your team must access to all the projects.

Each user would have to connect as GIT

git clone ssh://git@server/var/cache/git/essai001

And have their SSH private key working

Complex access control

For maximum control you can use ACLs and apply them selectively to specific repos

If your filesystem is ext2 or ext3, the filesystem will needto be mounted with the "acl" option. This can be done by editing /etc/fstab and changing something like:

/dev/md1 / ext3 defaults 1 1

to:

/dev/md1 / ext3 rw,acl 1 1

in my case

/dev/xvda2 / ext3 noatime,nodiratime,errors=remount-ro 0 1

became

/dev/xvda2 / ext3 noatime,nodiratime,errors=remount-ro,acl 0 1

Install acl on Debian

apt-get install acl

you need to remount the filesystem

mount -v -o remount /

We create a group of users allowed to read/write our repository Chris will be member of this group

groupadd git_test001_write
addgroup chris git_test001_write
groupadd git_test001_read
addgroup user2 git_test001_read

Give access only to these groups

chmod -R 700 /var/cache/git/test001
setfacl -R -m g:git_test001_write:rwX /var/cache/git/test001
find /var/cache/git/test001 -type d | xargs setfacl -R -m d:g:git_test001_write:rwX
setfacl -R -m g:git_test001_read:rX /var/cache/git/test001
find /var/cache/git/test001 -type d | xargs setfacl -R -m d:g:git_test001_read:rX

The first line close t The third line sets up the same permissions as a default ACL to be applied to any new files created in those directories. This access control is at the OS-level. Consequently there is no need to use git init --shared or otherwise set the core.sharedRepository config variable.

Explanation.

  • -R: apply recursively
  • -m: modify the ACL
  • g:<group>: permissions for group <group>
  • r: read access
  • w: write access
  • X: execute permissions, but only if the object is a directory, or already has execute permissions for some user
  • d:<spec>: set a default ACL (defined by <spec>) to be applied to any new files or directories added to the directory in the future

If you need to check what the current rights are, use getfacl

getfacl /var/cache/git/test001

Apache need it's own ACL if you still want to share your project via HTTPS Read only:

addgroup www-data git_test001_read

Read and Write:

addgroup www-data git_test001_write

NB: Apache need to be restarted or reloaded for each group change, a unix user (like Windows) record its groups at the login and keep them until next login.

/etc/init.d/apache2 reload

Personnally I also use ALL ACCESS groups:

groupadd git_all_write
groupadd git_all_read

Then give access to main folder (read only anyway)

setfacl -R -m g:git_all_write:rX /var/cache/git
setfacl -R -m g:git_all_read:rX /var/cache/git

and give access to all current and futur repositories

find /var/cache/git -type d | xargs setfacl -R -m d:g:git_all_write:rwX
find /var/cache/git -type d | xargs setfacl -R -m d:g:git_all_read:rX

Transfert of my existing work

Here is how I transfer my existing local XCode repositories: Git and XCode

Issues

Name of folder

if you name the folder /var/cache/git/project001.git instead of /var/cache/git/project001 the apache config won't work without changing the regular expressions and most of the apache configuration file

HTTP clone & push

I had the following message when I use "git clone" and I was still using WebDAV (useless nowaday) instead of the Smart Http mode

$git clone http://chris@git.company.com/test001.git/
Cloning into test001...
Password: 
warning: You appear to have cloned an empty repository.

The solution was to remember to do that

cd /var/cache/git/test001
git update-server-info
cd hooks
mv post-update.sample post-update

HTTPS and self-signed SSL Certificate

$ git clone https://chris@git.company.com/essai001.git/
Cloning into essai001...
Password: 
error: SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed while accessing https://chris@git.company.com/essai001.git/info/refs

fatal: HTTP request failed

I use self-signed certificate with Apache-SSL.

The solution is either:

  • cancelling the certificate validation from GIT (Not really an option)
  • indicating the certificate




Cancelling the validation is easy, add into ~/.gitconfig

[http]
    sslVerify=no


Indicating the certificate is almost as easy, add into ~/.gitconfig

[http]
    sslCAInfo=/Users/chris/.git.company.cer

NB: the certificate can be exported into PEM format if you use Firefox in “Preferences”->”Advanced”->”View Certificates”

References

Gitosis use for Users Management (French)