动机
前段时间st0rm23在自己的服务器上搭好了自己的gitlab,现在我准备开搞自己的web项目了。但是如果每次写完都要用一些文件传输的工具上传到自己的服务器就显得很低效是吧,也会降低自己写代码的动力。现在st0rm23就借着自己的gitlab,用一个webhook就可以自动将自己的gitlab上的代码部署到web目录下了。那么这样我们只要push一下代码到master上就可以自动生效了,用浏览器重新访问下网站就可以看了勒( •̀ ω •́ )y。
原理介绍
1、配置gitlab当push动作的时候,访问服务器上的一个链接比如xxx.example.com/boot.php
2、boot.php里面写着一行代码,会让服务器git pull相应项目的代码到web目录。
3、pull结束,代码就在web目录了,我们只要重新访问网站就可以了。
核心就是push的时候,gitlab会调用服务器上的脚本,服务器上的脚本就会从git重新拉取项目文件。当然有一些安全性的设计,下面就来详细地说明。
位置
啥?有同学找不到webhook,进入到某个项目中,然后点配置按钮(有个小齿轮),里面就有一个webhook配置,需要填链接地址和token。
配置服务端
先在服务端生成一对你的SSH密钥,因为之后服务器要用ssh方式免账号密码从gitlab上pull代码。用ssh-keygen在服务器上生成密钥,或者你已经有密钥了就跳过这一步。
$ ssh-keygen
有了密钥之后,复制你的公钥,在你的gitlab profile个人资料里,找到SSH的目录,粘贴保存进去就可以了。这样gitlab上就有了你web服务器的公钥了,就可以正常SSH了。
$ cat ~/.ssh/xx.pub ;这个可以看到公钥,xx.pub就是公钥文件(xx替换成你的文件名),把输出的东西复制到gitlab中
接着在服务端的相应目录下clone gitlab上的项目。这样就可以将这个文件夹对应到gitlab上的项目。后续的脚本就要根据这个对应关系来用脚本驱动这个git pull。
比如我在/var/www/的目录下git clone了gitlab上的项目,得到了/var/www/html/目录,然后将自己的web服务器nginx解析到/var/www/html/主目录(修改完配置文件之后,记得restart,不然不会生效的)。
git clone [your project address]
服务端脚本
服务端简易php脚本,只要php脚本在被访问的时候,执行git pull操作就可以从服务器pull代码了。
exec("cd /var/www/html/; git pull"); //先定位到相应目录,然后git pull
当然这个脚本太过简易了,被人恶意访问了怎么办,我们还是要加上一些验证。首先是token验证,访问的时候要有特定的token,我们才可以让这个访问生效,一般这个token可以在gitlab填,这样gitlab访问这个脚本的时候就会带上这个token就能识别出来了。有token还不够,被人知道了咋办,我们还要限制IP访问,只有gitlab服务器的ip我们才可以允许执行。修正完就成了下面这个样子。
<?php
$valid_token = 'secret_token';
$valid_ip = array('127.0.0.1'); //这里填你的gitlab服务器ip
$client_token = $_SERVER['HTTP_X_GITLAB_TOKEN'];
$client_ip = $_SERVER['REMOTE_ADDR'];
if ($client_token !== $valid_token) die('Token mismatch!');
if (!in_array($client_ip, $valid_ip)) die('Ip mismatch!');
exec("cd /var/www/html/; git pull origin master");
//exec("cd /var/www/html/; git pull origin master 2>&1", $output);
//var_dump($output); 这样可以用浏览器调试输出
?>
我们保存成/var/www/script/test.php。/var/www/script/这个目录等下我们是要公开给外部访问的。
服务端脚本地址
我们有了这个脚本之后,要部署到合适的位置。一般不是简单地放在web目录下(这个逻辑上也说不通是不是)。我们在web服务器上开一个端口4567来专门为这个脚本服务。下面以nginx为例。在/etc/nginx/sites-available/default的配置文件末尾加上这段
server {
listen 4567;
listen [::]:4567;
server_name example.com;
root /var/www/script;
index index.html;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
location / {
try_files $uri $uri/ =404;
}}
然后后检测完,重启一下nginx。
$ nginx -t
$ /etc/init.d/nginx restart
好,这时候就可以愉快地访问example.com:4567/test.php了。如果有东西返回了(一般是token mismatch因为我们根本没填token来访问的),那说明这个脚本架好了。
gitlab配置
好,我们现在已经有访问的脚本链接了,我们现在填到gitlab项目中的webhook一栏,保存下来就可以用了。
记得你的secret_token要和你test.php里面的配置一样哦,还有valid_ip地址的地方也要记得填对。如果你都填对了,你就在你本地的项目里新建一个文件,然后push到master分支,就可以在你的服务器web目录下愉快地看到你的项目文件了。这样一个自动部署环境就建好了,愉快地写代码吧少年。
账号权限
s0tm23我一开始兴致满满,觉得这样就可以了呀。结果一试,发现失败了。也不知道为啥,本着白盒测试的心态,代码在手,天下我有,那就手工调试输出看一下什么原因好了。结果发现是因为自己全程操作是在root用户下的,而nginx是运行在www用户下的。这TM就尴尬了,www的权限不够,不但无法访问.git文件(因为是root创建的),访问不到root的密钥连不上git,也无法修改root创建的项目文件。蛋疼死了,那就要用www用户重新走一遍流程。但是之前都做了那么多工作了,于是st0rm23就改了一下权限,让www用户也能正常使用。
找到web服务器执行用户
首先,我们要先找到nginx服务器用的是哪一只用户。先看一下配置文件,就可以看见第一行郝然写着用户是user www;这样我们就知道了exec里面的指令是由www用户执行的。
$ vi /etc/nginx/nginx.conf
如果在nginx.conf的第一行把运行用户改成user root;那么nginx就以root来运行了,所有问题就解决了。但是安全性太差了,人家要是挂个一句话木马什么的估计要乐死了,一进来就是root。所以不能用root呀,我们还是老实地给www配权限吧。
修改目录所有者
我之前一直都是用root操作的,结果www对.git的访问没有权限,/var/www/下的文件也没有修改的权限,这就TM尴尬了,这还怎么部署。所以我直接把整个/var/www/的所有权移交给www用户了,反正这个目录就是www的主目录(后面会提到这是怎么知道的),我们用chown指令修改一下就好了
$ chown -R www:www /var/www/
这样目录就可以移交给www用户了,现在www用户可以访问.git文件了,但是不幸的是www还是没有权限访问gitlab,因为它访问不到之前root的ssh密钥,我们就要切换到www用户,重新配置一下密钥。
切换到执行用户
接着我们我们切换到www用户
$ su www
结果显示“This account is currently not available.”,啥?这不让我登录?别急,这是因为linux连他们登录shell的权利都给剥夺了,我们暂时给www权利就好了。到/etc/passwd中,把www用户的配置修改一下,让他的登录shell变成/bin/bash
;www:x:22:22:www:/var/www:/usr/sbin/nologin
www:x:22:22:www:/var/www:/bin/bash
我们还可以观察到,www的用户是属于www用户组的,他的主目录为/var/www。记一下,待会有用的。然后www用户就有权限了。我们用su www重新切换一次,就会发现你现在是以www用户登录了。
重建密钥
之前生成的密钥是root的,www用户根本访问不到这个密钥,所有它连不上gitlab,提示没有权限访问。我们要重新生成一个,生成的步骤和本文开篇一致,不在缀述。有了密钥体系之后,还要尝试访问一下gitlab,让www记下对方的机器签名(之前我忘了这一步,匪夷所思了半天为啥我有密钥了还显示没有权限访问)。然后你会愉快地发现,www用户可以正常从gitlab访问数据了。再试一试脚本就可以正常使用了。
恢复执行用户的nologin
不要忘了在/etc/passwd中,把www用户的nologin选项恢复回去哟
www:x:22:22:www:/var/www:/usr/sbin/nologin
最后
哈哈哈,这回真的是可以用了,辛苦死宝宝了。