2009年5月29日 星期五

Svn + WebSvn + SSL + Trac on Leopard

參考文章:
svn + ssl + trac http://www.sonzea.com/articles/subversion-trac.html
python on svn http://trac.edgewall.org/wiki/TracOnOsxNoFink
Enable Apache + php http://foundationphp.com/tutorials/php_leopard.php
有群組權限設定的教學 http://october388.blogspot.com/2009/03/openssl-httpdsubversion.html

學生時代有實驗室的 server 可以用,工作後有公司的 server 可以用;除些之外, 自己寫寫 code 如果要控管的話,只有兩種方式:
1. 找免費的 svn server
2. 自己架

用 Mac mini 來架 server 不失為一個好方法,在 Leopard 已經內建 svn 1.4.4 和 Apache,要架一個 svn server 並非一件難事

Install subversion

首先準備一台有 Leopard 的 Mac 電腦,下載 svn 目前 Mac OS X binaries 最新版為1.6.2
我是下載 openCollabNet (Universal) 的版本
Includes:
* Complete Subversion 1.6 Installation
* Apache 2.2.x Subversion Modules
* Java, Perl, Python and Ruby Subversion Language Bindings
* Both repository access layers
* All repository data stores (Berkeley DB and FSFS)

打開 dmg 檔後執行 Subversion-1.6.2.pkg,點選"繼續"直到完成安裝,最後新版 svn 會安裝在 /opt/subversion 下,安裝完後可看一下 svn 版本是否正確,因為環境變數 PATH 此時並沒有包含/opt/subversion/bin,如果此時在 Terminal 執行 svn 會執行到 /usr/bin 下的舊版本 (1.4.4)。
1. 修改 ~/.bash_profile,在文件最後面加一行
export PATH=/opt/subversion/bin:$PATH

2. 重新登入後,檢查 svn 版本
$ svn --version
svn, version 1.6.2 (r37639)
compiled May 14 2009, 14:24:28

如果版本編號和新安裝的版本編號不同的話,可是會執用到舊版的 svn 喔。

Create a subversion repository

建立一個存放所有 svn repository 的目錄,/opt/subversion/repos 路徑隨便你建, 不要建到太奇怪的地方讓自己找不到就好了
$ cd /opt/subversion
$ mkdir repos
$ sudo svnadmin create /opt/subversion/repos/repoOne

下面的步驟是新增一個 tmp 目錄,連到 local 端的 svn server,checkout最新版本的 repoOne 專案下來,接著在 local 端建立三個子目錄 tags, branches, trunk,最後再將這三個子目錄 check-in 到本地端的 svn server上。

$ cd /opt/subversion/repos
$ mkdir tmp //建一個tmp目錄測試用
$ cd tmp
$ sudo svn co file:///opt/subversion/repos/repoOne //從指定的file路徑checkout抓最新版本的下來
$ cd repoOne //這個路徑是剛抓下來的專案根目錄
$ sudo svn mkdir tags branches trunk //隨便建了三個目錄
$ sudo svn ci -m "initial structure by sapp" // 把剛建好的三個目錄 check-in 到主機的 /opt/subversion/repos/repoOne

基本上做到這一步就大功告成了,你已經可以在你自己電腦的 local 端上使用 svn (但是我們的最終目的是架 svn server 讓多人同時使用)。

通常 svn server 也不會只建一個專案,以下指令建了另一個 sapprepo。

$ cd /opt/subversion
$ sudo svnadmin create /opt/subversion/repos/repoSapp
$ cd /opt/subversion/repos
$ mkdir tmpSapp
$ cd tmpSapp
$ sudo svn co file:///opt/subversion/repos/repoSapp
$ cd repoSapp
$ sudo svn mkdir tags branches trunk
$ sudo svn ci -m "initial sapprepo structure by sapp"

Enable Apache, PHP5, MySql

以往我都是使用 MAMP 架設我的 web server,一方面省麻煩,另一方面也不用設定一大堆東西,但是在 Leopard 中其實早就內建這些套件,只是沒有啟動而已,以下是兩者之間的版本差異。
MAMP 1.7:
# Apache 2.0.59
# PHP 5.2.5
# MySql 5.0.41

Leopard 10.5.7:
# Apache 2.2.6
# PHP 5.2.4
# MySql 5.0.45

今天我不使用 MAMP 的原因是因為它對 svn 的支援度不夠,必須要使用額外的模組,才能使 svn 正常運作 (其實也是我找那個模組找到累了)。

1. 啟動php,修改 /etc/apache2/httpd.conf 第 114 行
#LoadModule php5_module libexec/apache2/libphp5.so
ToLoadModule php5_module libexec/apache2/libphp5.so

2. 將 /etc/php.ini.default copy 一份放在 /etc/ 下,並重新命名為 php.ini
cd /etc
sudo cp php.ini.default php.ini

3. 修改 /etc/php.ini 第 305 行
error_reporting = E_ALL & ~E_NOTICE
Toerror_reporting = E_ALL

4. 打開 系統偏好設定 > 共享 > 將網頁共享選項打勾
網頁路徑預設為 DocumentRoot "/Library/WebServer/Documents"
若修改 listen port 80,SSL 認證時會得到以下Error
SSL 收到含超出最大允許字串長度的記錄。
(錯誤碼: ssl_error_rx_record_too_long)

5. 如果還需要 MySql 的話,再把MySql 啟動

Enable HTTP access

如果使用 MAMP,在這一個步驟會無法載入 mod_dav_svn.so 和 mod_authz_svn.so


1. 新增 httpd-subversion.conf 檔案,放在 /etc/apache2/extra
內文如下,前兩行是最新版 subversion 的 mod_dav_svn.so 和 mod_authz_svn.so 所在地,後半段設定子網域 repo 在主機上的實體路徑。
LoadModule dav_svn_module /opt/subversion/lib/svn-apache/mod_dav_svn.so
LoadModule authz_svn_module /opt/subversion/lib/svn-apache/mod_authz_svn.so

<location /repos>
DAV svn
SVNPath /opt/subversion/repos/repoOne
</location>

再將這兩行加到 httpd.conf 裡面,擺放的位置在 LoadModule ssl_module libexec/apache2/mod_ssl.so 之後即可。
# Subversion
Include /private/etc/apache2/extra/httpd-subversion.conf


ps: 如果要透過 http 來存取 svn 的話,svn repos 要 chown 為 _www。以本篇第一個新增的 repoOne 為例,需要將 /opt/subversion/repos/repoOne 的 owner 改為 _www,如此一來,Apache 才有權限去對這個目錄做修改。
sudo chown -R _www /opt/subversion/repos/repoOne

Enable SSL on your Web Server

因為 Leopard 預設為沒有把 SSL 打開,而 svn 的密碼是用明文的方式傳送,如果怕被人竊取密碼,可以自己產生憑證並打開 SSL 功能,使用 https 的方式登入。
以下步驟乍看之下有點煩鎖,實際上只需要照著指令一步一步執行。

1. 首先,產生自己的 key
$ openssl genrsa -des3 -out ca.key 4096
Generating RSA private key, 4096 bit long modulus
.......................................................................................................................++
.............................................................................................................................++
e is 65537 (0x10001)
Enter pass phrase for ca.key: // 設定 ca.key 密碼
Verifying - Enter pass phrase for ca.key:

2. 產生對應的 CA
$ openssl req -new -x509 -days 365 -key ca.key -out ca.crt
Enter pass phrase for ca.key: // 輸入 ca.key 密碼
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) [AU]:TW
State or Province Name (full name) [Some-State]:Taiwan
Locality Name (eg, city) []:Taipei
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Sapp
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:SappLiu
Email Address []:your_mail@gmail.com

3. 產生 server key
$ openssl genrsa -des3 -out server.key 4096
Generating RSA private key, 4096 bit long modulus
.............................................................++
.................................................................................++
e is 65537 (0x10001)
Enter pass phrase for server.key: // 設定 server.key 密碼
Verifying - Enter pass phrase for server.key:

4. 產生對應的 CSR,這裡必須特別注意,Common Name 必須打上你的 domain name,總之,就是主機的網址,例如: localhost, sapp.com.tw。
$ openssl req -new -key server.key -out server.csr
Enter pass phrase for server.key: // 輸入 server.key 密碼
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) [AU]:TW
State or Province Name (full name) [Some-State]:Taiwan
Locality Name (eg, city) []:Taipei
Organization Name (eg, company) [Internet Widgits Pty Ltd]:SappServer
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:your-domain-name
Email Address []:your_mail@gmail.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:y
string is too short, it needs to be at least 4 bytes long
A challenge password []:sapp
An optional company name []:

5. Sign the CSR with the CA
$ openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
Signature ok
subject=/C=TW/ST=Taiwan/L=Taipei/O=Sapp/CN=SappServer/emailAddress=your_mail@gmail.com
Getting CA Private Key
Enter pass phrase for ca.key: // 輸入 ca.key 密碼

6. Make an insecure version of the key
$ openssl rsa -in server.key -out server.key.insecure
Enter pass phrase for server.key: // 輸入 server.key 密碼
writing RSA key
$ mv server.key server.key.secure
$ mv server.key.insecure server.key
$ sudo cp server.key /etc/apache2/server.key
Password:
$ sudo cp server.crt /etc/apache2/server.crt
$ cd /etc/apache2
$ sudo chmod 600 server.key
$ sudo chmod 600 server.crt

7. 最後再修改 /etc/apache2/httpd.conf
#Include /private/etc/apache2/extra/httpd-ssl.conf
ToInclude /private/etc/apache2/extra/httpd-ssl.conf

8. 重新啟動 Apache (系統偏好設定 > 共享 > 網頁共享)。

不過因為這種憑證發放方式不是花錢向 VeriSign 這種第三方,公正數位憑證發行者買的,所以別人進到你的網頁會出現以下錯誤。請他直接新增例外就可以了,反正我們的主要目的是為了 svn 的密碼不外洩。
該憑證未受信任,因為簽發者憑證未知。
(錯誤碼: sec_error_unknown_issuer)

Make the Subversion repository require SSL

接下來讓 svn 支援 SSL,修改 /etc/apache2/extra/httpd-subversion.conf
<Location /repos>
DAV svn
SVNPath /opt/subversion/repos/repoOne
</Location>
To<Location /repos>
DAV svn
SVNPath /opt/subversion/repos/repoOne

# Require SSL connection for password protection.
SSLRequireSSL
</Location>

重新啟動 Apache後,連到 http://your-domain-name/repos,你會得到
Forbidden
You don't have permission to access /repos on this server.

因為我們沒設定帳號密碼,所以無法存取是正常的,請照著以下步驟新增帳號密碼
1. Create the authentication file.
$ sudo htpasswd -cm /etc/apache2/subversion.auth sapp
Password:
New password: // 輸入新增帳號的密碼
Re-type new password:
Adding password for user sapp
$ cd /etc/apache2/
$ sudo chmod 600 subversion.auth
$ sudo chown _www /etc/apache2/subversion.auth

若要新增另一組帳號密碼,參數用 -m 即可 (-m 以 md5 加密),否則會洗掉先前建立的帳號 sapp
$ sudo htpasswd -m /etc/apache2/subversion.auth jack

產生帳號後,修改 /etc/apache2/extra/httpd-subversion.conf
<Location /repos>
DAV svn
SVNPath /opt/subversion/repos/repoOne

# Require SSL connection for password protection.
SSLRequireSSL
</Location>
To<Location /repos>
DAV svn
SVNPath /opt/subversion/repos/repoOne

# Require SSL connection for password protection.
SSLRequireSSL

# how to authenticate a user
AuthType Basic
AuthName "Subversion repository"
AuthUserFile /private/etc/apache2/subversion.auth

# only authenticated users may access the repository
Require valid-user
</Location>

重新啟動 Apache 後,連到 https://your-domain-name/repos,就會跳出登入視窗讓你輸入帳號密碼。

特別加碼,顯示多個 svn 專案

我們若是建立多個 svn 專案,希望在 https://localhost/repos 列表顯示所有 svn 專案,可以修改/etc/apache2/extra/httpd-subversion.conf
<Location /repos>
DAV svn
SVNParentPath /opt/subversion/repos
SVNListParentPath on


# Require SSL connection for password protection.
SSLRequireSSL

# how to authenticate a user
AuthType Basic
AuthName "Subversion repository"
AuthUserFile /private/etc/apache2/subversion.auth

# only authenticated users may access the repository
Require valid-user
</Location>


Install WebSvn

下載 WebSvn 解壓縮後,放到 /Library/WebServer/Documents,將 include/distconfig.php 改名為 config.php,並修改 config.php,告訴 WebSvn 以下事情。

1. 執行的 svn binary 檔案在何處
因為我裝的 subversion 1.6.2 和內建於 leopard 1.4.4 的 PATH 不一樣
$config->setSVNCommandPath('/opt/subversion/bin');

2. 新建立的專案實體路徑的父目錄
$config->parentPath("/opt/subversion/repos");
或者可以更改顯示名稱
$config->addRepository('顯示名稱', '/opt/subversion/repos/repoOne');

3. 設定其它範本
$config->setTemplatePath("$locwebsvnreal/templates/BlueGrey/");
//$config->setTemplatePath("$locwebsvnreal/templates/calm/");

4. 設定 Tabs 需要顯示寬度
$config->expandTabsBy(4);

5. 新增 httpd-websvn.conf 放在 /private/etc/apache2/extra,讓 websvn 支援 SSL
<location websvn="">
# Require SSL connection for password protection.
SSLRequireSSL

# how to authenticate a user
AuthType Basic
AuthName "Subversion repository"
AuthUserFile /private/etc/apache2/subversion.auth

# only authenticated users may access the repository
Require valid-user

</location>

6. 在 /etc/apache2/httpd.conf 載入 WebSvn設定
# WebSvn
Include /private/etc/apache2/extra/httpd-websvn.conf

Install TRAC
如果沒有 gcc 的話,必須先安裝 Xcode

因為 Leopard 很貼心的內建 Python 和 SQLite,所以安裝 TRAC 只需再下載兩個東西,clearsilver 和 TRAC 並安裝就可以了。
下載clearsilver-0.10.5.tar.gz,解壓縮後使用 Terminal 進到clearsilver 目錄下編譯,然後安裝 (其中 --build=i386 這個參數是 intel-based 的電腦才需要加的 G5 以前的不需要)
$ tar xzf clearsilver-0.10.5.tar.gz
$ cd clearsilver-0.10.5
$ ./configure --build=i386 --disable-ruby --with-python=/usr/bin/python
$ make
$ sudo make install

接著進到 python 目錄執行 setup.py
$ cd python
$ python setup.py install

下載 Trac-0.11.4.tar.gz
$ sudo python ./setup.py install

到此為止,Trac 已經安裝完畢,接下來就只剩下設定 Trac,就大功告成了。通常在一台 svn server 上不會只有一個 svn 專案,因此我也將介紹如果用 Trac 管理多個 svn 專案的設定。不過為了簡化複雜度,使接下來的文章有條有序,先介紹單一 svn 專案的設定方式,再介紹多個 svn 專案的設定。

1. 首先新增一個目錄用來放置所有 Trac 專案
$ sudo mkdir /usr/local/trac


2. 接下來使用 trac-admin 指令來產生 Trac 專案,以下是產生一個名為 "Hello Trac" 的 Trac 專案,其存放的路徑在 /usr/local/trac/FirstTrac。
$ sudo trac-admin /usr/local/trac/FirstTrac initenv
Project Name [My Project]> Hello Trac
Database connection string [sqlite:db/trac.db]> // 預設使用 sqlite,除非你要用MySql,否則直接不用改
Repository type [svn]> // 直接回傳,除非你不是用 svn
Path to repository [/path/to/repos]> /opt/subversion/repos/repoOne // 想要管理的 svn 專案目錄路徑 (之前建好的)

3. 修改 owner 為 _www
$ sudo chown -R _www /usr/local/trac/FirstTrac

3.1 通常這個時候可以啟動測試,不過因為有權限的問題,因為我們已經把 owner 改為 _www,如果下此指令的 User 不是 _www 的話,測試會出現沒有 read _and_ write permission 的 Error
$ tracd --port 8000 /usr/local/trac/FirstTrac

然後連到 http://localhost:8000/FirstTrac 做測試。

3.2 因為我已經更新了 svn 1.6.2, 而 python 預設支援的版本並沒有更新, 所以在這裡我們必須把以下兩個目錄
/opt/subversion/lib/svn-python/libsvn
/opt/subversion/lib/svn-python/svn
複製到
/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python


4. 為 Apache 設定 FastCGI 來跑 TRAC
將早先下載的Trac-0.11.4.tar.gz 解壓縮後,把 Trac-0.11.4/cgi-bin/trac.fcgi 複製/Librarys/WebServer/CGI-Executables

新增一個檔案 httpd-fastcgi.conf 放在 /private/etc/apache2/extra,設定 FastCGI,內容為:
# Enable fastcgi for .fcgi files
<IfModule mod_fastcgi.c>
AddHandler fastcgi-script .fcgi
FastCgiIpcDir /private/var/run/fastcgi
</IfModule>

LoadModule fastcgi_module libexec/apache2/mod_fastcgi.so

再新增一個檔案 httpd-trac.conf 放在 /private/etc/apache2/extra,設定 Trac,內容為:
ScriptAlias /trac /Library/WebServer/CGI-Executables/trac.fcgi
FastCgiConfig -initial-env TRAC_ENV=/usr/local/trac/FirstTrac

<Location /trac>
SetEnv TRAC_ENV /usr/local/trac/FirstTrac
SetEnv PYTHON_EGG_CACHE /tmp
</Location>

<Directory /Library/WebServer/CGI-Executables>
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>

其中 SetEnv PYTHON_EGG_CACHE /tmp 的設定是為了權限問題,預設位置在 /Library/WebServer,但是 Apache 在這個路徑並沒有 mkdir 的權限,因此更改 PYTHON_EGG_CACHE 的路徑到 /tmp 中。

最後再更改 /etc/apache2/httpd.conf 裡面的設定,載入剛新增的兩個設定檔。
# FastCGI
Include /private/etc/apache2/extra/httpd-fastcgi.conf

# TRAC
Include /private/etc/apache2/extra/httpd-trac.conf

5. 同樣的,如果還想為 Trac 加上 SSL,就必須再修改 /private/etc/apache2/extra/httpd-trac.conf
ScriptAlias /trac /Library/WebServer/CGI-Executables/trac.fcgi
FastCgiConfig -initial-env TRAC_ENV=/usr/local/trac/FirstTrac

<Location /trac>
SetEnv TRAC_ENV /usr/local/trac/FirstTrac
SetEnv PYTHON_EGG_CACHE /tmp
# Require SSL connection for password protection.
SSLRequireSSL

AuthType Basic
AuthName "TRAC Project"
AuthUserFile /private/etc/apache2/subversion.auth

Require valid-user
</Location>

<Directory /Library/WebServer/CGI-Executables>
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>

特別加碼,建立多個 Trac 專案管理多個 svn 專案

以下設定是已經建立一個 Trac 專案的前提下去做修改的,為了不贅述,只做重點說明。
$ sudo trac-admin /usr/local/trac/SecondTrac initenv
Project Name [My Project]> Sapp Trac
Database connection string [sqlite:db/trac.db]>
Repository type [svn]>
Path to repository [/path/to/repos]> /opt/subversion/repos/repoSapp // 另一個 svn 專案

修改 owner 為 _www
$ sudo chown -R _www /usr/local/trac/SecondTrac

設定 Trac
修改檔案 /private/etc/apache2/extra/httpd-trac.conf
ScriptAlias /trac /Library/WebServer/CGI-Executables/trac.fcgi
FastCgiConfig -initial-env TRAC_ENV_PARENT_DIR=/usr/local/trac

<Location /trac>
SetEnv TRAC_ENV_PARENT_DIR /usr/local/trac
SetEnv PYTHON_EGG_CACHE /tmp

# Require SSL connection for password protection.
SSLRequireSSL

AuthType Basic
AuthName "TRAC Project"
AuthUserFile /private/etc/apache2/subversion.auth

Require valid-user
</Location>

<Directory /Library/WebServer/CGI-Executables>
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>


最後再重新啟動 Apache (系統偏好設定 > 共享 > 網頁共享),然後連到 https://localhost/trac 就會發現有兩個 Trac 專案可以選擇。Hello Trac 和 Sapp Trac。

小插曲

整個安裝完成後,我使用 Eclipse 去 checkout 檔案,發現每次都會出現 Error,還以為是我那邊的權限沒有開。後來才發現是我的 subclipse 沒有更新,更新到 1.6.x 之後就一切正常了。

3 意見:

Xerothermic 提到...

謝謝你的websvn教學,這真是很有用的網站!

Sapp 提到...

終於有人體會到我的用心了 :-D

馬可 提到...
作者已經移除這則留言。