svnserve是一個輕型的伺服器,可以同客戶端通過在TCP/IP基礎上的自行定義有狀態協議通訊,客戶端通過使用開頭為svn://或者svn+ssh://svnserve的URL來訪問一個svnserve伺服器。這一小節將會解釋運行svnserve的不同方式,客戶端怎樣實現伺服器的認證,怎樣設定版本庫恰當的訪問控制。
有許多不同方法運行svnserve:
作為一個獨立系統服務(daemon)啟動svnserve,監聽請求。
當特定埠號收到一個請求,就會使UNIX的inetd系統服務(daemon)臨時調用svnserve處理。
使用SSH在加密通道發起臨時svnserve服務。
以Windows service服務方式運行svnserve。
使用svnserve最簡單的方式是作為獨立「守護」進程運行,使用-d選項:
$ svnserve -d $ # svnserve is now running, listening on port 3690
當以守護模式運行svnserve時,你可以使用--listen-port=和--listen-host=選項來自行定義「綁定」的埠號和主機名。
一旦svnserve已經運行,它會將你系統中所有版本庫發佈到網路,一個客戶端需要指定版本庫在URL中的絕對路徑,舉個例子,如果一個版本庫是位於/usr/local/repositories/project1,則一個客戶端可以使用svn://host.example.com/usr/local/repositories/project1來進行訪問,為了提高安全性,你可以使用svnserve的-r選項,這樣會限制只輸出指定路徑下的版本庫,例如:
$ svnserve -d -r /usr/local/repositories …
使用-r可以有效地改變文件系統的根位置,客戶端可以使用去掉前半部分的路徑,留下的要短一些的(更加有提示性)URL:
$ svn checkout svn://host.example.com/project1 …
如果你希望inetd啟動進程,你需要使用-i(--inetd)選項,在這個例子裡,我們顯示了在指令列中運行svnserve -i的輸出,但是請注意這不是如何實際啟動daemon; 請繼續閱讀例子後的文章,學習如何設定inetd啟動svnserve。
$ svnserve -i ( success ( 1 2 ( ANONYMOUS ) ( edit-pipeline ) ) )
當用參數--inetd調用時,svnserve會嘗試使用自行定義協議通過stdin和stdout來與Subversion客戶端通話,這是使用inetd工作的標準方式,IANA為Subversion協議保留3690埠號,所以在類Unix系統你可以在/etc/services新增如下的幾行(如果不存在的話):
svn 3690/tcp # Subversion svn 3690/udp # Subversion
如果系統是使用經典的類Unix的inetd系統服務(daemon),你可以在/etc/inetd.conf新增這幾行:
svn stream tcp nowait svnowner /usr/bin/svnserve svnserve -i
確定「svnowner」用戶擁有訪問版本庫的適當權限,現在如果一個客戶連接來到你的伺服器的埠號3690,inetd會產生一個svnserve進程來做服務。當然,你也可以新增-r到指令列,限制暴露出的版本庫。
第三種方式使用-t選項的「管道模式」,這個模式假定一個分佈式服務程序如RSH或SSH已經驗證了一個用戶,並且以這個用戶調用了一個私有svnserve進程,svnserve運作如常(通過stdin和stdout通訊),並且可以設想通訊是自動轉向到一種通道並傳遞回客戶端,當svnserve被這樣的通道代理調用,確定認證用戶對版本資料庫有完全的讀寫權限,這與本地用戶通過file:///URl訪問版本庫同樣重要。
這個選項將在「穿越 SSH 隧道」一節詳細討論。
如果你的Windows系統是Windows NT (2000, 2003, XP, Vista)的後代,你可以將svnserve作為Windows服務運行,這是比使用--daemon (-d)選項直接運行系統服務(daemon)感覺更好。使用系統服務(daemon)模式,需要打開指令列窗口,輸入命令,然後保持指令列窗口不關閉,而作為Windows服務時,在後台運行,可以在啟動時自動執行,並且可以使用同其他Windows服務一致的管理界面啟動和停止服務。
你需要使用指令列工具SC.EXE定義新的服務,就像inetd的設定行,你必須在Windows啟動時指明svnserve的調用:
C:\> sc create svn
binpath= "C:\svn\bin\svnserve.exe --service -r C:\repos"
displayname= "Subversion Server"
depend= Tcpip
start= auto
這樣定義了一個新的Windows服務,叫做「svn」,會在啟動時(在這個例子裡,根目錄是C:\repos。)執行特定的svnserve.exe,可是前面這個例子產生了一些錯誤。
首先,要注意svnserve.exe必須使用--service選項啟動。svnserve的其它選項必須在同一行上指定,但你不能使用衝突的選項,例如--daemon (-d)、--tunnel或--inetd (-i),而選項-r或--listen-port都沒有問題。第二,調用SC.EXE時必須注意空格:key= value的模式中key=之間必須沒有空格,而且在與value之間只能有一個空格。最後,必須注意執行的指令列中的空格,如果目錄名中包含了空格(或其它需要迴避的字元),為了迴避這些字元,請將整個binpath值放在雙引號中:
C:\> sc create svn
binpath= "\"C:\program files\svn\bin\svnserve.exe\" --service -r C:\repos"
displayname= "Subversion Server"
depend= Tcpip
start= auto
也需要注意單詞binpath會造成誤解—它的值是一個指令列,而不是可執行的路徑,所以我們為了防止有嵌入的空格而使用了引號圍繞。
一旦定義了服務,就可以使用標準GUI工具(服務管理控制面板)進行停止、啟動和查詢,或者是通過指令列:
C:\> net stop svn C:\> net start svn
也可以通過刪除其定義刪除服務:sc delete svn,只需要確定首先停止服務,SC.EXE有許多子命令和選項,更多訊息可以運行sc /?查看。
如果一個客戶端連接到svnserve進程,如下事情會發生:
客戶端選擇特定的版本庫。
伺服器處理版本庫的conf/svnserve.conf文件,並且執行裡面定義的所有認證和授權政策。
依賴於位置和授權政策,
如果沒有收到認證請求,客戶端可能被允許匿名訪問,或者
客戶端收到認證請求,或者
如果操作在「通道模式」,客戶端會宣佈自己已經在外部得到認證。
在撰寫本文時,伺服器還只知道怎樣發送CRAM-MD5[38]認證請求,本質上講,就是伺服器發送一些資料到客戶端,客戶端使用MD5哈希算法建立這些資料組合密碼的指紋,然後返回指紋,伺服器執行同樣的計算並且來計算結果的一致性,真正的密碼並沒有在互聯網上傳遞。
當然也有可能,如果客戶端在外部通過通道代理認證,如SSH,在那種情況下,伺服器簡單的檢驗作為那個用戶的運行,然後使用它作為認證用戶名,更多訊息請看「穿越 SSH 隧道」一節。
像你已經猜測到的,版本庫的svnserve.conf文件是控制認證和授權政策的中央機構,這文件與其它設定文件格式相同(見「運行設定區」一節):小節名稱使用方括號標記([和]),註釋以井號(#)開始,每一小節都有一些參數可以設定(variable = value),讓我們瀏覽這個文件並且學習怎樣使用它們。
此時,svnserve.conf文件的[general]部分包括所有你需要的變數,開始先定義一個保存用戶名和密碼的文件和一個認證域:
[general] password-db = userfile realm = example realm
realm是你定義的名稱,這告訴客戶端連接的「認證命名空間」,Subversion會在認證提示裡顯示,並且作為憑證快取(見「客戶端憑證快取」一節。)的關鍵字(還有伺服器的主機名和埠號),password-db參數指出了保存用戶和密碼列表文件,這個文件使用同樣熟悉的格式,舉個例子:
[users] harry = foopassword sally = barpassword
password-db的值可以是用戶文件的絕對或相對路徑,對許多管理員來說,把文件保存在版本庫conf/下的svnserve.conf旁邊是一個簡單的方法。另一方面,可能你的多個版本庫使用同一個用戶文件,此時,這個文件應該在更公開的地方,版本庫分享用戶文件時必須設定為相同的域,因為用戶列表本質上定義了一個認證域,無論這個文件在哪裡,必須設定好文件的讀寫權限,如果你知道運行svnserve的用戶,限定這個用戶對這個文件有讀權限是必須的。
svnserve.conf有兩個或多個參數需要設定:它們確定未認證(匿名)和認證用戶可以做的事情,參數anon-access和auth-access可以設定為none、read或者write,設定為none會限制所有方式的訪問,read允許只讀訪問,而write允許對版本庫完全的讀/寫權限,例如:
[general] password-db = userfile realm = example realm # anonymous users can only read the repository anon-access = read # authenticated users can both read and write auth-access = write
實例中的設定實際上是參數的預設值,你一定不要忘了設定它們,如果你希望更保守一點,你可以完全封鎖匿名訪問:
[general] password-db = userfile realm = example realm # anonymous users aren't allowed anon-access = none # authenticated users can both read and write auth-access = write
服務進程不僅僅理解對版本庫的整體訪問控制,也可以細粒度的控制版本庫某個文件或目錄的訪問,為了使用這個特性,你需要定一個包含詳細規則的文件,並將變數authz-db指向到這個文件。
[general] password-db = userfile realm = example realm # Specific access rules for specific locations authz-db = authzfile
authzfile得語法會在「基於路徑的授權」一節討論,注意變數authz-db並不比anon-access和auth-access更高級,如果定義了所有的變數,要想被允許訪問必須滿足所有的規則。
svnserve的內建認證會非常容易得到,因為它避免了建立真實的系統帳號,另一方面,一些管理員已經建立好了SSH認證框架,在這種情況下,所有的項目用戶已經擁有了系統帳號和有能力「SSH到」伺服器。
SSH與svnserve結合很簡單,客戶端只需要使用svn+ssh://的URL模式來連接:
$ whoami harry $ svn list svn+ssh://host.example.com/repos/project harry@host.example.com's password: ***** foo bar baz …
在這個例子裡,Subversion客戶端會調用一個ssh進程,連接到host.example.com,使用用戶harry認證,然後會有一個svnserve私有進程以用戶harry運行。svnserve是以管道模式調用的(-t),它的網路協議是通過ssh「封裝的」,被管道代理的svnserve會知道程序是以用戶harry運行的,如果客戶執行一個提交,認證的用戶名會作為版本的參數保存到新的修訂本。
這裡要理解的最重要的事情是Subversion客戶端不是連接到運行中的svnserve系統服務(daemon),這種訪問方法不需要一個運行的系統服務(daemon),也不需要在必要時喚醒一個,它依賴於ssh來發起一個svnserve進程,然後網路斷開後終止進程。
當使用svn+ssh://的URL訪問版本庫時,記住是ssh提示請求認證,而不是svn客戶端程序。這意味著密碼不會有自動快取(見「客戶端憑證快取」一節),Subversion客戶端通常會建立多個版本庫的連接,但用戶通常會因為密碼快取特性而沒有注意到這一點,當使用svn+ssh://的URL時,用戶會為ssh在每次建立連接時重複的詢問密碼感到討厭,解決方案是用一個獨立的SSH密碼快取工具,像類Unix系統的ssh-agent或者是Windows下的pageant。
當在一個管道上運行時,認證通常是基於操作系統對版本庫資料庫文件的訪問控制,這同Harry直接通過file:///的URL直接訪問版本庫非常類似,如果有多個系統用戶要直接訪問版本庫,你會希望將他們放到一個常見的組裡,你應該小心的使用umasks。(確定要閱讀「支持多種版本庫訪問方法」一節)但是即使是在管道模式時,文件svnserve.conf還是可以阻止用戶訪問,如設定auth-access = read或auth-access = none。[39]
你會認為SSH管道的故事該結束了,但還不是,Subversion允許你在運行設定文件config(見「運行設定區」一節)建立一個自行定義的管道行為方式,舉個例子,假定你希望使用RSH而不是SSH,在config文件的[tunnels]部分作如下定義:
[tunnels] rsh = rsh
現在你可以通過指定與定義匹配的URL模式來使用新的管道定義:svn+rsh://host/path。當使用新的URL模式時,Subversion客戶端實際上會在後台運行rsh host svnserve -t這個命令,如果你在URL中包括一個用戶名(例如,svn+rsh://username@host/path),客戶端也會在自己的命令中包含這部分(rsh username@host svnserve -t),但是你可以定義比這個更加智能的新的管道模式:
[tunnels] joessh = $JOESSH /opt/alternate/ssh -p 29934
這個例子裡論證了一些事情,首先,它展現了如何讓Subversion客戶端啟動一個特定的管道程序(這個在/opt/alternate/ssh),在這個例子裡,使用svn+joessh://的URL會以-p 29934參數調用特定的SSH程序—對連接到非標準埠號的程序非常有用。
第二點,它展示了怎樣定義一個自行定義的環境變數來覆蓋管道程序中的名字,設定SVN_SSH環境變數是覆蓋預設的SSH管道的一種簡便方法,但是如果你需要為多個伺服器做出多個不同的覆蓋,或許每一個都聯繫不同的埠號或傳遞不同的SSH選項,你可以使用本例論述的機制。現在如果我們設定JOESSH環境變數,它的值會覆蓋管道中的變數值—會執行$JOESSH而不是/opt/alternate/ssh -p 29934。
不僅僅是可以控制客戶端調用ssh方式,也可以控制伺服器中的sshd的行為方式,在本小節,我們會展示怎樣控制sshd執行svnserve,包括如何讓多個用戶分享同一個系統帳戶。
作為開始,定位到你啟動svnserve的帳號的主目錄,確定這個賬戶已經安裝了一套SSH公開/私有密鑰對,用戶可以通過公開密鑰認證,因為所有如下的技巧圍繞著使用SSHauthorized_keys文件,密碼認證在這裡不會工作。
如果這個文件還不存在,建立一個authorized_keys文件(在UNIX下通常是~/.ssh/authorized_keys),這個文件的每一行描述了一個允許連接的公鑰,這些行通常是下面的形式:
ssh-dsa AAAABtce9euch.... user@example.com
第一個字段描述了密鑰的類型,第二個字段是未加密的密鑰本身,第三個字段是註釋。然而,這是一個很少人知道的事實,可以使用一個command來處理整行:
command="program" ssh-dsa AAAABtce9euch.... user@example.com
當command字段設定後,SSH系統服務(daemon)運行命名的程序而不是通常Subversion客戶端詢問的svnserve -t。這為實施許多伺服器端技巧開啟了大門,在下面的例子裡,我們簡寫了文件的這些行:
command="program" TYPE KEY COMMENT
因為我們可以指定伺服器端執行的命令,我們很容易來選擇運行一個特定的svnserve程序來並且傳遞給它額外的參數:
command="/path/to/svnserve -t -r /virtual/root" TYPE KEY COMMENT
在這個例子裡,/path/to/svnserve也許會是一個svnserve程序的包裹腳本,會來設定umask(見「支持多種版本庫訪問方法」一節)。它也展示了怎樣在虛擬根目錄定位一個svnserve,就像我們經常在使用系統服務(daemon)模式下運行svnserve一樣。這樣做不僅可以把訪問限制在系統的一部分,也可以使用戶不需要在svn+ssh://URL裡輸入絕對路徑。
多個用戶也可以共享同一個帳號,作為為每個用戶建立系統帳戶的替代,我們建立一個公開/私有密鑰對,然後在authorized_users文件裡放置各自的公鑰,一個用戶一行,使用--tunnel-user選項:
command="svnserve -t --tunnel-user=harry" TYPE1 KEY1 harry@example.com command="svnserve -t --tunnel-user=sally" TYPE2 KEY2 sally@example.com
這個例子允許Harry和Sally通過公鑰認證連接同一個的賬戶,每個人自行定義的命令將會執行。--tunnel-user選項告訴svnserve -t命令採用命名的參數作為經過認證的用戶,如果沒有--tunnel-user,所有的提交會作為共享的系統帳戶提交。
最後要小心:設定通過公鑰共享賬戶進行用戶訪問時還會允許其它形式的SSH訪問,即使你設定了authorized_keys的command值,舉個例子,用戶仍然可以通過SSH得到shell訪問,或者是通過伺服器執行X11或者是埠號轉發。為了給用戶盡可能少的訪問權限,你或許希望在command命令之後指定一些限制選項:
command="svnserve -t --tunnel-user=harry",no-port-forwarding,\
no-agent-forwarding,no-X11-forwarding,no-pty \
TYPE1 KEY1 harry@example.com