Subversion 安全点滴
 

2009-08-07 来源:linuxing 来源:linuxfly.org

 
经过前面的讲解,相信大家对Subversion的使用也基本了解。但之前讲述的svn://协议和http://采用的都是明文方式,这可能会带来安全问题。我们可以采用svn+ssh或Apache+SSL的方式增强安全性。

一、Subversion的传输协议
Subversion可以支持以下多种传输协议,以便客户端和仓库之间进行数据版本控制:
 
引用
file:// 本地数据传输
http:// 基于Apache的HTTP协议(明文)
https:// 基于Apache+SSL的HTTPS协议
svn:// 基于svnserve的SVN协议(明文)
svn+ssh:// 基于SSH的svnserve协议

可见,正如telnet与ssh方式的差别一样,Subversion也可以采用加密的传输方式。
那该选择哪种加密方式呢?在 这里 会告诉你两种方式的差异。

二、实现SVN over SSH方式
要使用svn+ssh://方式前,我们先列举一些该方式的优缺点:
 
引用
1、网络协议是有状态的,svn+ssh比WebDAV快很多;
2、该方式可以利用现有的ssh帐号和用户基础;
3、只有一个认证方法可选择,并且需要用户在同一个系统组,使用共享ssh密钥;
4、没有任何类型的日志,甚至是错误,如果使用不正确,会导致文件许可问题。

另外,在使用该方式的注意事项是:
 
引用
1、svn客户端在访问远端时,默认除保存验证信息外,还会保留协议类型,以方便下次提交版本更新时不需再次定义;
但这就是说,你不能这次使用svn+ssh://,而下次用svn:// ;
2、该方式会同时进行两重验证,一是ssh的密钥验证,二是svnserve设置的访问权限认证,缺一不可;
3、该方式是依赖于ssh来发起一个svnserve进程,然后网络断开后终止进程,所以,远端是不需要单独启动svnserve服务的;
4、因此,连接是需要直接指向版本仓库在远端的实际物理路径,而不是svnserve给出的映射路径

让我们看一个最简单的例子:

1、设置权限访问文件
为了验证上面提到的,该方式是由ssh发起的,并且需要经过两重认证。所以,我们看看版本库的访问权限文件,有:
 
引用
# pwd
/var/svn/repos/conf
# cat authz
[/]
linuxing = rw
root = r

这里表明我们只给予root用户读的权限,而给予linuxing读写的权限。

2、只读测试
我们以root用户来执行:
 
引用
# svn list svn+ssh://192.168.228.135/var/svn/repos
root@192.168.228.135's password:
project1/
project2/

可见,会提示需要进行以root用户登录的密钥验证。
※ 注意,与直接使用svn svn://的方式不同,这里不会提示输入其他用户名,而直接使用执行svn命令的当前用户。
另外,svn+ssh://后面的路径,为远端数据仓库在服务器上的实际路径


接下来,我们把版本仓库Checkout出来:
 
引用
# svn checkout svn+ssh://192.168.228.135/var/svn/repos/project1
取出修订版 6。

3、写入测试
 
引用
# cd project1/
# touch test3.pl
# svn add *
A         test3.pl
# svn status
A      test3.pl
# whoami
root
# svn commit -m 'test'
root@192.168.228.135's password:
svn: 提交失败(细节见下):
svn: 授权失败

可见,由于在authz文件中,只给予root只读的权限,版本更新的提交动作验证失败了。由此也可以看到,之前执行chekcout时的用户信息、验证类型等都已被保存下来,供commit时直接引用。

试试把root=r 改为root=rw,再试一次:
 
引用
# svn commit -m 'test'
root@192.168.228.135's password:
新增           test3.pl
传输文件数据.
提交后的修订版为 7。

成功了。这证明确实如开头部分所说的,该方式由ssh发起,并采用ssh密钥认证,成功后,会由ssh启动一个svnserve进程,因此,访问版本库的权限还受特定版本库的访问文件限制。其验证用户信息,由执行svn客户端的用户决定。

那能否在svn客户端这边指定传输用的用户名呢?
由于该方式是由当前用户的ssh发起的,所以,svn的--username等参数似乎没用:
 
引用
# svn commit -m 'test' --username linuxing
root@192.168.228.135's password:

但这可能与svn的版本有关,没仔细研究。
另外,如果希望在svnserve端就控制该方式的执行用户、密钥,ssh参数等,可以在远端中对svnserve.conf和command变量进行配置,详细请见:这里
※ 如果觉得每次都需要输入密码太麻烦,可以使用ssh-keygen生成密钥对,做成ssh的密钥认证即可。(注意,由于svn和svnserve是可以根据配置使用不同的用户执行的,所以该认证必须是在执行svn与运行svnserve服务用户之间进行。)

三、实现Apache+SSL方式
可见,虽然svn+ssh方式速度比较快,但由于涉及多方的信息和权限检验,如ssh服务验证、svnserve访问权限、文件系统本身权限等。这会带来相当复杂的权限问题。而Apache+SSL方式的优缺点在于:
 
引用
1、允许Subversion使用大量已经集成到Apache的用户认证系统,而不需要在服务器创建系统帐号;
2、只要配置Apache的网络传输通过SSL加密,HTTP(S) 通常可以穿越公司防火墙;
3、通过web浏览器可访问内置的版本库浏览,版本库还可以作为网络驱动器加载,实现透明的版本控制;
4、拥有完全的Apache日志记录;
5、当然,由于HTTP是无状态的协议,需要更多的传递,速度会比svnserve方式慢。

所以,除非你有一个严重依赖于SSH帐号的基础,而且你的用户已经在服务器上有了帐号,那建立一个通过ssh的svnserve方案就非常有意义,否则,我们不会建议这种方案。通常还是通过svnserve或Apache管理的用户访问版本库比较安全,而不是使用完全的系统帐户。如果你很希望加密的通讯,那可能还是需要选择这个方案,但我们更加推荐SSL的Apache方案。
对于大量的用户,Apache与现有的认证系统(LDAP、Active Directory等)的集成也是非常值得考虑的情况。

好了,之前的日志已经介绍了Apache+mod_dav_svn方式的配置,若要采用SSL加密传输,所增加内容,实际是在配置Apache为自签名认证。

1、原理
商业应用需要越过公司防火墙的版本库访问,防火墙需要小心的考虑非认证用户“吸取”他们的网络流量的情况,SSL让那种形式的关注更不容易导致敏感数据泄露。
如果Subversion使用OpenSSL编译,它就会具备与Subversion服务器使用https://的URL通讯的能力,Subversion客户端使用的Neon库不仅仅可以用来验证服务器证书,也可以必要时提供客户端证书,如果客户端和服务器交换了SSL证书并且成功地互相认证,所有剩下的交流都会通过一个会话关键字加密。
当通过https://与Apache通讯时,一个Subversion客户端可以接收两种类型的信息:
 
引用
● 一个服务器证书
● 一个客户端证书的要求

当Subversion命令行客户端选择信任该证书,那么客户端与服务端之间就创建了一个基于SSL的加密传输。之后的访问权限控制等,与简单的HTTP协议方式是完全相同的。

2、默认配置
以红旗 Asianux 3.0为例,若系统已经安装了openssl 和mod_ssl 包,那么系统提供的httpd服务就已经支持SSL传输。
这时,你可以访问https://ip/看看,如果已经做好mod_dav_svn的配置,还可以直接用https://ip/repos访问对应的版本库。

从/etc/httpd/conf.d/ssl.conf中可看到证书存放的位置:
 
引用
SSLCertificateFile /etc/pki/tls/certs/localhost.crt
SSLCertificateKeyFile /etc/pki/tls/private/localhost.key

当然,你还可以创建自己的证书,以替换系统自带的。

3、创建自己的证书
创建证书可使用openssl 套件进行,一般方法有两种:
 
引用
a、在/etc/pki/tls/certs目录下,利用Makefile的方式,使用make server.crt创建;
b、直接使用openssl 命令创建。

两种方式创建证书是相同的,我们用openssl 方式试试。

首先,进入存放证书的路径,创建密钥:
 
引用
# cd /etc/pki/tls/certs
# openssl genrsa -des3 1024 > myserver.key
Generating RSA private key, 1024 bit long modulus
....................++++++
...............++++++
e is 65537 (0x10001)
Enter pass phrase:
Verifying - Enter pass phrase:

默认需要输入pass phrase(不能少于4个字符)。但如果今后Apache使用该证书时,也会提示输入pass phrase,这样会带来交互性问题。所以,我们需要去掉pass phrase:
 
引用
# openssl rsa -in myserver.key -out myserver.new.key
Enter pass phrase for myserver.key:
writing RSA key
# mv myserver.new.key myserver.key
mv:是否覆盖“myserver.key”? y

然后,根据私钥myserver.key,生成证书请求.csr文件:
 
引用
# openssl req -new -key myserver.key -out myserver.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [GB]:
State or Province Name (full name) [Berkshire]:GuangDong
Locality Name (eg, city) [Newbury]:GuangZhou
Organization Name (eg, company) [My Company Ltd]:RedFlag Linux Ltd
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:qktest
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

建议在输入主机名时,设置为与运行Apache的机器主机名相同。
最后,由钥匙生成证书:
 
# openssl req -x509 -days 3650 -key myserver.key -in myserver.csr -out myserver.crt

证书有效期为3650天,即10年。至此,证书创建完毕。
把私钥移动到相对安全的目录下,并给予安全的权限:
 
# mv myserver.key /etc/pki/tls/private/
# chmod 600 /etc/pki/tls/private/myserver.key

最后,修改/etc/httpd/conf.d/ssl.conf文件,把默认的证书,改为自己的证书:
 
引用
SSLCertificateFile /etc/pki/tls/certs/myserver.crt
SSLCertificateKeyFile /etc/pki/tls/private/myserver.key

重新启动httpd服务即可。

使用浏览器访问https://ip/repos,结果:
(由于不是CA等正式机构颁发的证书,会有警告提示)
点击在新窗口中浏览此图片
选择“继续浏览”,如果你拥有该版本库的访问权限,就可以看到库中的内容:
点击在新窗口中浏览此图片
留意地址栏中,为https://协议。从浏览器中可查看相关的证书信息:
点击在新窗口中浏览此图片
点击在新窗口中浏览此图片

也可通过svn客户端,如TortoiseSVN浏览:
点击在新窗口中浏览此图片
即可正常访问版本仓库:
点击在新窗口中浏览此图片

这时,可以同时使用HTTP和HTTPS两种协议访问版本库。
若访问密码的传输也使用SSL加密方式,可在/etc/httpd/conf.d/subversion.conf中的<Location>标签部分加入:
 
引用
SSLRequireSSL

保存后,重启httpd服务即可。

另外,如果希望除本地网络(192.168.228.x段)外的机器必须使用HTTPS协议才能访问版本库,还可以在上面提到的<Location>标签部分加入:
 
引用
RewriteEngine        on
RewriteCond          %{REMOTE_ADDR} !^192\.168\.228\.[0-9]+$
RewriteCond          %{HTTPS} !=on
RewriteRule          .* - [F]

四、附录
另一种生成证书的方法,就是除了先生成私钥,然后再生成请求文件的方式外,反过来也是可以的:
 
# openssl req -new > server.csr
# openssl rsa -in privkey.pem -out server.key
# openssl req -x509 -days 3650 -key server.key -in server.csr > server.crt

其中,会自动创建一个privkey.pem带pass phrase的私钥文件。第二步就是去掉该私钥的pass phrase。两种创建证书的方式是完全一样的。

五、参考文档
使用Subversion进行版本控制(书)
Apache SSL/TLS Encryption
Apache-SSL 如何创建自己的证书

火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织