安装 gitolite
apt-get install gitolite
初始化 gitolite
设置 git 用户
adduser --system \
--shell /bin/bash \
--group \
--disabled-password \
--gecos 'git version control' \
--home /var/git \
git
初始化 git 仓库
scp ~/.ssh/id_rsa.pub root@server:/tmp/admin.pub
su - git
gl-setup /tmp/admin.pub
管理和添加项目
git clone git@server:gitolite-admin.git
conf/gitolite.conf
keydir/admin.pub
repo gitolite-admin
RW+ = admin
repo testing
RW+ = @all
------------------------
搭建git服务器-gitolite
我搭建的服务器框架大致如下图:
那么以下的搭建操作就是基于这个图进行配置。每个节点都是ubuntu 16.04发行版,图示有4个节点
节点 | 功能 |
---|---|
Server | 中心化的git仓库,本文假设IP为192.168.100.100 |
Alice | 网管,负责创建仓库或者各种访问权限 |
Bob | 项目组长,负责Code-Review和Merge分支到master,拥有修改仓库权限 |
Carl | 项目开发者,只能在自己分支上面修改 |
搭建服务器
搭建服务器操作在Server和Alice节点进行Alice端
生成一个rsa密钥对cd /tmp
ssh-keygen -t rsa -b 4096 -C "alice"
假设生成的公私钥为# 私钥
~/.ssh/alice
# 公钥
~/.ssh/alice.pub
将私钥写入当前用户ssh配置文件中vi ~/.ssh/config
# 添加
Host server
User git
Hostname 192.168.100.100
Port 22
ServerAliveInterval 30
IdentityFile ~/.ssh/alice
将公钥上传到Server备用scp ~/.ssh/alice.pub root@server:/tmp/
Server端
留意gitolite的README,提到依赖的软件,有最低版本的要求- git 1.6.6 or later
- perl 5.8.8 or later
- openssh 5.0 or later
adduser git
su git
cd ~
创建空的ssh配置目录mkdir -p ~/.ssh
克隆gitolite仓库git clone https://github.com/sitaramc/gitolite
cd gitolite
创建一个目录存放gitolite二进制文件,然后安装mkdir -p ~/bin
./install -to ~/bin
设置Alice的公钥,这样Alice就成为了gitolite管理员~/bin/gitolite setup -pk /tmp/alice.pub
执行上面一条命令后,/tmp/alice.pub 被拷贝到~/.gitolite/keydir目录下,并且仓库gitolite-admin克隆到本地后,keydir目录也有一份alice.pub。所有仓库存放在
~/repositories
下,gitolite会自动修改~/.ssh/authorized_keys
实现不同用户的访问。因此单独使用一个git用户的目的是,不希望用户手动修改authorized_keys里面的内容,而是通过gitolite来间接修改它。
仓库创建与权限
修改访问权限在Alice节点进行克隆admin仓库,因为服务器只有Alice的公钥,其它用户无权访问。
cd ~
git clone git@server:gitolite-admin
cd gitolite-admin
直接编辑这个conf文件实现权限管理conf/gitolite.conf
详细的权限和仓库创建可以参考官方README:http://gitolite.com/gitolite/conf
https://github.com/sitaramc/gitolite#adding-users-and-repos
比如我修改为repo foo
RW+ = bob
- master = carl
- refs/tags/v[0-9] = carl
RW+ carl = carl
R = carl
那么达到的效果是:- 创建了一个名字为foo的仓库
- RW+表示可读可写可overwrite,Bob拥有仓库最大权限
- 减号说明Carl没有master分支和tags的读写权限
- Carl只能在自己分支(carl分支)上面进行修改,拥有carl分支的最大权限
- Carl可以读取其它分支,这时候就可以读master分支了
carl_ssh_key.pub
那么等号后面的内容就不是carl,而是carl_ssh_key为了与conf/gitolite.conf中的帐户对应,创建ssh公私鈅要保存为正确的文件名。
# 输出id_rsa的时候,保存为~/my_gitolite_keys/bob
ssh-keygen -t rsa -b 4096 -C "bob"
# 输出id_rsa的时候,保存为~/my_gitolite_keys/carl
ssh-keygen -t rsa -b 4096 -C "carl"
把~/my_gitolite_keys/中对应的公私鈅交给Bob和Carl,下面测试步骤,要用到公私鈅将公钥文件添加到gitolite-admin仓库中
cp ~/my_gitolite_keys/*.pub ~/gitolite-admin/keydir/
修改conf和生成密钥完毕,就可以commit,然后将新配置push给server端cd ~/gitolite-admin
git add *
git commit -m "add user Bob, Carl; generate keys"
git push
那么server端在push结束后自动执行perl脚本,实现权限管理。测试
测试git在Bob和Carl节点进行将由Alice交给Bob和Carl的公私鈅,分别存放到各自节点的.ssh目录下
节点 | 公私鈅存放路径 |
---|---|
Bob | 公钥 ~/.ssh/bob.pub 私钥 ~/.ssh/bob |
Carl | 公钥 ~/.ssh/carl.pub 私钥 ~/.ssh/carl |
Bob节点
将私钥写入当前用户ssh配置文件中vi ~/.ssh/config
# 添加
Host server
User git
Hostname 192.168.100.100
Port 22
ServerAliveInterval 30
IdentityFile ~/.ssh/bob
本地克隆服务器上的foo仓库,测试修改代码cd ~
git clone git@server:foo.git
cd foo
echo "hello world" > README.md
git add README.md
git commit -m "print hello world"
git push
这样即验证了Bob有读写服务器master分支的权限Carl节点
将私钥写入当前用户ssh配置文件中vi ~/.ssh/config
# 添加
Host server
User git
Hostname 192.168.100.100
Port 22
ServerAliveInterval 30
IdentityFile ~/.ssh/carl
本地克隆服务器上的foo仓库,测试修改代码cd ~
git clone git@server:foo.git
cd foo
echo "try to modify branch master" > README.md
git add README.md
git commit -m "invalid commit"
git push
这么做push的话会被拒绝,即验证了Carl没有写服务器master分支的权限。下面测试在carl分支的工作流程
情况1: 如果上游没有carl分支,可以添加一个carl分支并Push到服务器上
git checkout -b carl
echo "create branch carl" > README.md
git add README.md
git commit -m "branch: carl created"
git push --set-upstream origin carl
情况2: 如果上游已经存在carl分支,直接切换到carl分支git checkout carl
经常性使用git pull以拉取服务器上最新的代码版本。安全设置
修改默认的shell为gitolite专用的,而不是默认的bashsudo vi /etc/passwd
# 修改git用户的shell程序为/home/git/bin/gitolite-shell
不允许git用户使用密码登陆sudo vi /etc/ssh/sshd_config
# 增加以下
Match User git
PasswordAuthentication no
# 重启ssh服务
sudo service ssh restart
参考链接
ssh key配置gitolite-README
gitolite搭建git仓库管理服务
Enabling git access via password authentication with gitolite
We recently started using gitolite at my workplace. Our previous git hosting setup involved manually managing linux users and groups on the Ubuntu server, which was needlessly time consuming and painful. There were times when file permissions got out of sync because the repo was deployed by user X from his workstation (where group permissions were not setup correctly for a shared git repository), and other headaches. <–more–> We needed something easier. We did not want to go to a workflow where there is one git user on the system, every employee authenticates with their own public key to that user, and everyone with a key has access to everything. We wanted more granular control, but also an easier management workflow. While gitolite does use a single system user under the covers, it provides per-user access control (governed by public keys), easy repo management, and a couple other nice features like wildcard repositories and branch-level access restrictions.
However, one of our developers is in the habit of accessing a random server, cloning a repo (using his password), and pushing from environments other than his development environment. Obviously, his private key is not going to be on each of the machines he does this from. Gitolite does not allow password-based access for obvious reasons — it needs to know who you are to determine permissions — so gitolite was cramping his style.
There are a couple ways around this:
Add a public key for each system he pushes changes from. Since he often logs in as “administrator” or “root” on individual systems, this obviously is not a good solution.
Have the developer enable SSH Agent Forwarding from his machine. This works well, as long as the user does not chain sessions (connect to server A, from there to server B, and clone on B) or is willing / able / allowed to enable Agent Forwarding on each system in the chain. (NOTE: on Mac OS X Snow Leopard, enabling SSH Agent Forwarding is as simple as running ssh-add and adding two lines to your ~/.ssh/config file:
# add key to agent (I do this in ~/.profile) ssh-add $HOME/.ssh/id_rsa # ~/.ssh/config Host * ForwardAgent yes
Come up with something else.
What we really wanted was a way to allow password-based access to the repositories. Since gitolite does not support this itself, we had to come up with another way. As it turns out, doing so is not too bad, though it does involve a little bash shell black magic.
What we came up with is creating a system user on the git server for each developer who wants password access to git repositories, using a custom script as their defined shell (instead of /bin/sh or /bin/bash). Don’t forget to make your new script executable.
This script needs to look at what the user is trying to do (git action, gitolite ADC, some random shell command, nothing…) and act accordingly. Here’s the script we came up with, which will be dissected below to explain what each part does in more detail than the inline doc (gist).
#!/bin/bash
shift # get rid of -c
# if no commands, just open a shell
if [[ $# -eq 0 ]]; then
/bin/bash -l
# if the first arg is a git- command, that means it is something like git-push, etc... so forward it
elif [[ $1 == git-* ]]; then
ssh -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no git@localhost $*
# if the first arg is SET_ENV_ONLY, we sourced this file in order to set up the gitolite function
elif [[ $1 == "SET_ENV_ONLY" ]]; then
gitolite () {
ssh -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no git@localhost $*
}
# if there is at least one non-git command, source this file in a new shell and create the 'gitolite' function
else
/bin/bash -c "source $0 shiftme SET_ENV_ONLY; $*"
fi
If the user is not trying to do anything (just wants to open a shell), then there are no arguments and we should just open a bash prompt:
if [[ $# -eq 0 ]]; then
/bin/bash -l
Okay, the simple bit is out of the way. Now on to the black magic.
If the user is trying to perform a git command (via git push, etc.) then we create a new SSH session to localhost (remember, this is running on the same server as gitolite) and pass along the commands ($*). Since the user has already logged in, this ssh command will run from their account using their private / public key. The git commands are things like git-receive-pack, git-upload-pack, etc, so we match the first script argument against “git-“.
Finally, we don’t care about validating the host’s fingerprint (we are already on the host) and there can be no man in the middle (since we are going to localhost), so we can turn off StrictHostKeyChecking. We also don’t want to print out the various fingerprint messages that SSH will generate when you ignore host keys, so we make the new SSH connection quiet. That’s it for this block:
elif [[ $1 == git-* ]]; then
ssh -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no git@localhost $*
I am going to go over the last two pieces in reverse order, because it is easier to follow.
If the user sent some SSH commands that were not native git commands, we need to process those commands. So, we will execute the remaining commands in a new bash process.
BUT, what if the user is trying to perform a gitolite command (such as expand or an ADC), what should we do? We can’t just trust that all commands the user sends will either be a git command or a gitolite command, so we need to set up some more black magic to handle that for us. So the first thing we will do when we open a new bash process to handle the remaining commands is source this custom script ($0) with a custom value we made up (SET_ENV_ONLY). That way, if this script executes again we will know it was done by ourselves and we can set up some more magic.
else
/bin/bash -c "source $0 shiftme SET_ENV_ONLY; $*"
Finally for the last block.
If our new script is being executed, and the first argument is SET_ENV_ONLY (after _shift_ing), then it is because we sourced it from our new bash process from the previous step. What we do now is set up a gitolite command which does something very similar to what happens if the user is performing a native git command: it opens an SSH connection to git@host and performs the gitolite commands.
After this has been done, “gitolite expand” is the same as “ssh git@git.company.com expand”. This makes it so the developer can still run gitolite commands and ADCs, he just has to prefix each command with gitolite. Something like this:
$ ssh [DEVELOPER]@git.company.com "gitolite expand; echo $PATH; gitolite info"
is equivalent to this:
$ ssh git@git.company.com expand
$ ssh [DEVELOPER]@git.company.com echo $PATH
$ ssh git@git.company.com info
Now you just need to create accounts that use this script as their shell. Each user can add whatever public keys they want to their authorized_keys file, or they can authenticate solely with their password. The only requirement is that their server account has a private / public key generated, and that the public key is added to gitolite.
$ ssh root@git.company.com
$ useradd -s /usr/local/bin/custom_gitbash.sh -m [DEVELOPER]
$ su [DEVELOPER]
$ ssh-keygen -t rsa
$ cp ~/.ssh/id_rsa.pub ~/[DEVELOPER]@git.company.com.pub
# add the new public key to the gitolite keydir