### svnserve,一個自定義的服務器
**svnserve**是一個輕型的服務器,可以同客戶端通過在TCP/IP基礎上的自定義有狀態協議通訊,客戶端通過使用開頭為`svn://`或者`svn+ssh://`**svnserve**的URL來訪問一個**svnserve**服務器。這一小節將會解釋運行**svnserve**的不同方式,客戶端怎樣實現服務器的認證,怎樣配置版本庫恰當的訪問控制。
### 調用服務器
有許多調用**svnserve**的方式,如果調用時沒有參數,你只會看到一些幫助信息,然而,如果你計劃使用**inetd**啟動進程,你可以傳遞`-i`(`--inetd`)選項:
~~~
$ 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**守護進程,你可以在`/etc/inetd.conf`添加這幾行:
~~~
svn stream tcp nowait svnowner /usr/bin/svnserve svnserve -i
~~~
確定“svnowner”用戶擁有訪問版本庫的適當權限,現在如果一個客戶連接來到你的服務器的端口3690,**inetd**會產生一個**svnserve**進程來做服務。
在一個Windows系統,有第三方工具可以將**svnserve**作為服務運行,請看Subversion的網站的工具列表。
**svnserve**的第二個選項是作為獨立“守護”進程,為此要使用`-d`選項:
~~~
$ svnserve -d
$ # svnserve is now running, listening on port 3690
~~~
當以守護模式運行**svnserve**時,你可以使用`--listen-port=`和`--listen-host=`選項來自定義“綁定”的端口和主機名。
也一直有第三種方式,使用`-t`選項的“管道模式”,這個模式假定一個分布式服務程序如**RSH**或**SSH**已經驗證了一個用戶,并且*以這個用戶*調用了一個私有**svnserve**進程,**svnserve**運作如常(通過*stdin*和*stdout*通訊),并且可以設想通訊是自動轉向到一種通道傳遞回客戶端,當**svnserve**被這樣的通道代理調用,確定認證用戶對版本數據庫有完全的讀寫權限,(見[服務器和訪問許可:一個警告]( "服務器和訪問許可:一個警告")。)這與本地用戶通過`file:///`URl訪問版本庫同樣重要。
**服務器和訪問許可:一個警告**
首先需要記住,一個Subversion版本庫是一組數據庫文件,任何進程直接訪問版本庫需要對整個版本庫有正確的讀寫許可,如果你不仔細處理,這會變得很頭痛,特別是當你使用Berkeley DB數據庫而不是FSFS時,詳細信息可以閱讀[“支持多種版本庫訪問方法”一節]( "支持多種版本庫訪問方法")。
第二點,當配置**svnserve**、Apache **httpd**或者其它任何服務器時,不要使用`root`用戶(或者其它具備無限制權限的用戶)啟動服務器進程,根據所有權和版本庫允許的權限,通常應該創建一個新的自定義用戶,例如很多管理員會創建一個叫做`svn`的用戶,賦予這個用戶排他的擁有權和對Subversion版本庫的導出權利,只讓服務器以這個用戶運行。
一旦**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
…
~~~
### 內置的認證和授權
如果一個客戶端連接到**svnserve**進程,如下事情會發生:
-
客戶端選擇特定的版本庫。
-
服務器處理版本庫的`conf/svnserve.conf`文件,并且執行里面定義的所有認證和授權政策。
-
依賴于位置和授權政策,
-
如果沒有收到認證請求,客戶端可能被允許匿名訪問,或者
-
客戶端收到認證請求,或者
-
如果操作在“通道模式”,客戶端會宣布自己已經在外部得到認證。
在撰寫本文時,服務器還只知道怎樣發送CRAM-MD5認證請求,本質上講,就是服務器發送一些數據到客戶端,客戶端使用MD5哈希算法創建這些數據組合密碼的指紋,然后返回指紋,服務器執行同樣的計算并且來計算結果的一致性,*真正的密碼并沒有在互聯網上傳遞。*
當然也有可能,如果客戶端在外部通過通道代理認證,如**SSH**,在那種情況下,服務器簡單的檢驗作為那個用戶的運行,然后使用它作為認證用戶名,更多信息請看[“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
~~~
注意**svnserve**只能識別“整體”的訪問控制,一個用戶可以有全體的讀/寫權限,或者只讀權限,或沒有訪問權限,沒有對版本庫具體路徑訪問的細節控制,很多項目和站點,這種 訪問控制已經完全足夠了,然而,如果你希望單個目錄訪問控制,你會需要使用包括**mod_authz_svn**(見[“鉤子腳本”一節]( "鉤子腳本"))的Apache,或者是使用**pre-commit**鉤子腳本來控制寫訪問(見[“鉤子腳本”一節]( "鉤子腳本")),Subversion的分發版本包含一個**commit-access-control.pl**和一個更加復雜的**svnperms.py**腳本可以作為pre-commit腳本使用。
### SSH認證和授權
**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**守護進程,這種訪問方法不需要一個運行的守護進程,也不需要在必要時喚醒一個,它依賴于**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`。
你會認為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配置技巧
不僅僅是可以控制客戶端調用**ssh**方式,也可以控制服務器中的**sshd**的行為方式,在本小節,我們會展示怎樣控制**sshd**執行**svnserve**,包括如何讓多個用戶分享同一個系統帳戶。
#### 初始設置
作為開始,定位到你啟動**svnserve**的帳號的主目錄,確定這個賬戶已經安裝了一套SSH公開/私有密鑰對,用戶可以通過公開密鑰認證,因為所有如下的技巧圍繞著使用SSH`authorized_keys`文件,密碼認證在這里不會工作。
如果這個文件還不存在,創建一個`authorized_keys`文件(在UNIX下通常是`~/.ssh/authorized_keys`),這個文件的每一行描述了一個允許連接的公鑰,這些行通常是下面的形式:
~~~
ssh-dsa AAAABtce9euch.... user@example.com
~~~
第一個字段描述了密鑰的類型,第二個字段是未加密的密鑰本身,第三個字段是注釋。然而,這是一個很少人知道的事實,可以使用一個`command`來處理整行:
~~~
command="program" ssh-dsa AAAABtce9euch.... user@example.com
~~~
當`command`字段設置后,SSH守護進程運行命名的程序而不是通常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**,就像我們經常在使用守護進程模式下運行**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
~~~
見RFC 2195。
- 第1章介紹
- Subversion的歷史
- Subversion的特性
- Subversion的架構
- 安裝Subversion
- Subversion的組件
- 快速入門
- 第2章基本概念
- 版本模型
- Subversion實戰
- 摘要
- 第3章指導教程
- 導入
- 修訂版本: 號碼、關鍵字和日期,噢,我的!
- 初始化的Checkout
- 基本的工作周期
- 檢驗歷史
- 其他有用的命令
- 摘要
- 第4章分支與合并
- 使用分支
- 在分支間拷貝修改
- 常見用例
- 轉換工作拷貝
- 標簽
- 分支維護
- 摘要
- 第5章版本庫管理
- 版本庫的創建和配置
- 版本庫維護
- 添加項目
- 摘要
- 第6章配置服務器
- 網絡模型
- svnserve,一個自定義的服務器
- httpd,Apache的HTTP服務器
- 支持多種版本庫訪問方法
- 第7章高級主題
- 屬性
- Peg和實施修訂版本
- 外部定義
- 賣主分支
- 本地化
- Subversion版本庫URL
- 第8章開發者信息
- 使用API
- 進入工作拷貝的管理區
- WebDAV
- 使用內存池編程
- 為Subversion做貢獻
- 第9章Subversion完全參考
- svn add
- svn blame
- svn cat
- svn checkout
- svn cleanup
- svn commit
- svn copy
- svn delete
- svn diff
- svn export
- svn help
- svn import
- svn info
- svn list
- svn log
- svn merge
- svn mkdir
- svn move
- svn propdel
- svn propedit
- svn propget
- svn proplist
- svn propset
- svn resolved
- svn revert
- svn status
- svn switch
- svn update
- svnadmin
- svnadmin create
- svnadmin deltify
- svnadmin dump
- svnadmin help
- svnadmin hotcopy
- svnadmin list-dblogs
- svnadmin list-unused-dblogs
- svnadmin load
- svnadmin lstxns
- svnadmin recover
- svnadmin rmtxns
- svnadmin setlog
- svnadmin verify
- svnlook
- svnlook author
- svnlook cat
- svnlook changed
- svnlook date
- svnlook diff
- svnlook dirs-changed
- svnlook help
- svnlook history
- svnlook info
- svnlook log
- svnlook propget
- svnlook proplist
- svnlook tree
- svnlook uuid
- svnlook youngest
- svnserve
- svnversion
- mod_dav_svn Configuration Directives
- 附錄A.Subversion對于CVS用戶
- 目錄的版本
- 更多離線操作
- 區分狀態和更新
- 分支和標簽
- 元數據屬性
- 沖突解決
- 二進制文件和轉化
- 版本化的模塊
- 認證
- 轉化CVS版本庫到Subversion
- 附錄C.WebDAV和自動版本化
- 自動版本化交互性
- Subversion和DeltaV
- 術語表