外部非公開のSVNサーバ(Subversion)に、公開サーバを踏み台にしてアクセスする

なんていうんでしょう、えーと。こんにちは。
mogwai超カッコいい。それより。
ソースの世代管理にバージョン管理システムを導入しました。
当初は普通にリモートサーバ(以降の呼称は開発サーバ)にリポジトリサーバ(以降の呼称はSVNサーバ)をインストールしようと思ったのですが、昔買った玄箱がほとんど働いていないので、この際そっちにいれることにしました。
うちでは開発サーバは本番サーバを兼ねているので(本当は開発兼本番サーバと言うべきかもしれないけど)外部公開していますが、玄箱は公開していません。閉じたネットワーク内でファイルサーバとして使っていましたが、今後もそのかたちは変えたくありません。
ところが、今回の開発は僕以外に外部の共同開発者も開発に参加しているため、外側からもSVNサーバにアクセスできるようにしなければなりません。
相関図にするとこうなります。
Relationship
図の右下の「管理者」が僕です。リレーションが薄くひいてあるのは外部非公開の意味で、外からは「管理者」のPCと「SVNサーバ」はアクセスできないようになっています。
外部の開発者がいなければ「管理者」が直接プライベートネットワーク内の「SVNサーバ」にアクセスすれば事足りるのですが、このままだと「外部開発者A」と「外部開発者B」が「SVNサーバ」にアクセスできません。
じゃあ「SVNサーバ」を公開しよう、というのはなしです。「SVNサーバ」を玄箱に置かずにやっぱり「開発」サーバに置こう、というのの100場合くらいない話です。
なので、「SVNサーバ」は引き続き外部非公開のまま、公開済の「開発サーバ」を踏み台にして、「SVNサーバ」を使用できるように構築します。
経路にするとこうなります。
Routing
では以下その手順です。
■前提条件
管理者のアカウント名は「inner」とする。すでに作成済
外部開発者のアカウント名は「outer」とする。未作成
開発サーバのホスト名は「devel」
SVNサーバのホスト名は「svn」
■SVNサーバの構築
・概要
SVNサーバにはSubversionをインストール
Subversionは開発サーバのTrac「redmine」からHTTPアクセスできるようにするため、apacheもインストール
Subversion操作ユーザのアカウント名は「repos」
SVNプロジェクトは「project」
・手順
apache操作ユーザの作成

inner@svn:~$ sudo /usr/sbin/groupadd -g 500 www
inner@svn:~$ sudo /usr/sbin/useradd -g www -s /sbin/nologin apache
inner@svn:~$ sudo /usr/bin/passwd -d apache

apacheのインストール

inner@svn:~$ sudo apt-get install apache2
inner@svn:~$ sudo vi /etc/apache2/apache2.conf
# ↓追加
ServerName svn:80
inner@svn:~$ sudo vi /etc/apache2/envvars
# ↓変更
export APACHE_RUN_USER=apache
export APACHE_RUN_GROUP=www
inner@svn:~$ sudo /etc/init.d/apache2 restart
# apache操作ユーザで起動していることを確認
inner@svn:~$ ps aux | grep apache | grep -v grep
# HTTPステータスコード200が返ることを確認
inner@svn:~$ wget http://localhost -O -

SSHの鍵認証と許可ユーザの設定

inner@svn:~$ sudo vi /etc/ssh/sshd_config
# ↓変更
PermitRootLogin no
PermitEmptyPasswords no
PasswordAuthentication no
# ↓追加
AllowUsers inner repos
inner@svn:~$ sudo /etc/init.d/ssh reload

Subversionのインストール(SVN用のapacheモジュールもインストール)

inner@svn:~$ sudo apt-get install subversion
inner@svn:~$ sudo apt-get install subversion-devel
inner@svn:~$ sudo apt-get install subversion-tools
inner@svn:~$ sudo apt-get install subversion-mod_dav_svn
inner@svn:~$ sudo apt-get install libapache2-svn
# dav_svnがapacheモジュール配下に置かれているか確認
inner@svn:~$ ll /usr/lib/apache2/modules | grep dav
inner@svn:~$ find /etc/apache2/ -name dav_svn.conf
# 置かれていなかったのでシンボリックリンクで回避
inner@svn:~$ sudo ln -s /usr/lib/apache2/modules /etc/apache2/modules
inner@svn:~$ sudo vi /etc/apache2/mods-enabled/dav_svn.conf
# ↓追加
<Location /repos>
DAV svn
SVNPath /usr/local/repos/
</Location>
inner@svn:~$ sudo /etc/init.d/apache2 reload

Subversion操作ユーザの作成

inner@svn:~$ sudo /usr/sbin/useradd -g www -d /home/repos -m repos
inner@svn:~$ sudo /usr/bin/passwd -d repos

リポジトリの作成

inner@svn:~$ sudo -u apache mkdir /usr/local/repos
inner@svn:~$ sudo chmod 0775 /usr/local/repos
inner@svn:~$ sudo -u repos svnadmin create /usr/local/repos
inner@svn:~$ ll /usr/local/repos

Subversion操作ユーザによる動作確認

inner@svn:~$ svn mkdir file:///usr/local/repos/project -m "create project"
inner@svn:~$ svn mkdir file:///usr/local/repos/project/trunk -m "create trunk"
inner@svn:~$ svn mkdir file:///usr/local/repos/project/tags -m "create tags"
inner@svn:~$ svn mkdir file:///usr/local/repos/project/branches -m "create branches"
inner@svn:~$ svn list file:///usr/local/repos/project/branches
inner@svn:~$ svn list http://svn/repos/project/trunk

■外部開発者のアカウント用意
・概要
鍵はSSH-2 RSA形式。管理者PCのPuTTYで事前に作って、秘密鍵だけ開発サーバにアップしておく
操作は開発サーバ「devel」でおこなう (途中からSVNサーバに乗り換えます)
・手順
外部開発者用のユーザ作成
# 外部開発者×nはすべてグループ「mate」に属する。パスワードは適当に。いずれも省略。

[inner@devel~]$ sudo /usr/sbin/useradd -g mate -d /home/outer -m outer
[inner@devel~]$ sudo /usr/bin/passwd outer
[inner@devel~]$ sudo mkdir /home/outer/.ssh
[inner@devel~]$ sudo mv ~/id_rsa.pub /home/outer/.ssh/authorized_keys
[inner@devel~]$ sudo chown -R outer:mate /home/outer/.ssh
[inner@devel~]$ sudo chmod 0700 /home/outer/.ssh
[inner@devel~]$ sudo chmod 0600 /home/outer/.ssh/authorized_keys

管理者PCからouterユーザで、PuTTYの鍵認証でログインできることを確認しておく(略)
開発サーバからSVNサーバに、SSH鍵認証でログインできるようにする

[inner@devel~]$ sudo scp -p /home/outer/.ssh/authorized_keys svn:~/
[inner@devel~]$ ssh -A svn
inner@svn:~$ sudo mkdir /home/repos/.ssh
# 管理者の公開鍵をSubversion操作ユーザに渡しておく
inner@svn:~$ sudo cp -p ~/.ssh/authorized_keys /home/repos/.ssh/authorized_keys
# 外部開発者の公開鍵をSubversion操作ユーザに渡しておく
inner@svn:~$ sudo mv ~/authorized_keys /home/repos/.ssh/authorized_keys2
inner@svn:~$ sudo chown -R repos:www /home/repos/.ssh
inner@svn:~$ sudo chmod 0700 /home/repos/.ssh
inner@svn:~$ sudo chmod 0600 /home/repos/.ssh/authorized_keys*
inner@svn:~$ sudo -u repos mkdir /home/repos/workspace

ログインできることの確認(SVN操作の確認も)

inner@svn:~$ exit
[inner@devel~]$ ssh -A repos@svn
repos@svn:~$ exit
[inner@devel~]$ sudo -u outer ssh -A repos@svn
repos@svn:~$ cd workspace
repos@svn:~/workspace$ svn co file:///usr/local/repos/project/trunk .
repos@svn:~/workspace$ vi test.txt
repos@svn:~/workspace$ svn add test.txt
repos@svn:~/workspace$ svn commit -m "test"
repos@svn:~/workspace$ exit

本当は、外部開発者はjailkitでホームDIR配下に閉じ込めて、使用できるコマンドもsshとsvnとその他適当なコマンドに制限させたかったのですが、ログイン後のsshがどうしても利用できるようにできず、断念しました。
詳細は省きますが、今後の課題として残りました。
■開発サーバにSVNクライアントをインストール
・概要
SVNクライアントにはSubversionを利用
開発サーバのTrac「redmine」がSVNサーバのリポジトリにHTTP経由でアクセスするための設定
なお、HTTP経由となっていますが、開発サーバがHTTPでアクセスするのはサーバサイドなので、開発サーバからSVNサーバの80番ポートがプライベートネットワーク内で見えていれば、SVNサーバは引き続き外部非公開で問題ありません(特にTracアプリケーションを使わなければ必要ないと思います)。
・手順
SVNクライアントをインストール

[inner@devel~]$ cd /usr/local/src
[inner@devel~/src]$ wget http://subversion.tigris.org/downloads/subversion-1.6.13.tar.gz
[inner@devel~/src]$ tar zxvf subversion-1.6.13.tar.gz
[inner@devel~/src]$ wget http://subversion.tigris.org/downloads/subversion-deps-1.6.13.tar.gz
[inner@devel~/src]$ tar zxvf subversion-deps-1.6.13.tar.gz
[inner@devel~/src]$ cd subversion-1.6.13
# SVNクライアントのみインストールします
[inner@devel~/subversion-1.6.13]$ ./configure --without-berkeley-db --without-apxs \
[inner@devel~/subversion-1.6.13]$ --without-swig --without-serf --with-ssl
[inner@devel~/subversion-1.6.13]$ make
[inner@devel~/subversion-1.6.13]$ sudo make install
[inner@devel~/subversion-1.6.13]$ cd ~/
[inner@devel~]$ svn list http://svn/repos/project

■クライアントPCにSVNクライアントをインストール
・概要
SVNクライアントにはTortoiseSVNを利用
クライアントPCはWindowsVISTA
・手順
TortoiseSVNのインストール

http://www.gside.org/Gentoo/subversion/subversion_client.html

上記の手順を元に以下をインストールしました

http://sourceforge.net/projects/tortoisesvn/files/Application/1.6.11/TortoiseSVN-1.6.11.20210-win32-svn-1.6.13.msi/download

http://sourceforge.net/projects/tortoisesvn/files/Language%20Packs/1.6.11/LanguagePack_1.6.11.20210-win32-ja.msi/download

■クライアントPCから開発サーバ経由でSVNサーバにアクセス
・概要
PuTTY(ごった煮版)はインストール済
トンネルに利用するポートは20022とする
手順では開発者アカウント「outer」を設定(管理者「inner」は省略)
・手順
まず、開発サーバのSSHログイン設定をおこないます。
putty.exeをダブルクリック、「PuTTY設定」を以下のように設定
セッション
Putty_devel_session

ホスト名:devel
ポート:22
接続タイプ:SSH
セッション一覧:任意の名前(例:devel)

接続=>データ

自動ログインのユーザ名:outer

SSH=>認証
Putty_devel_auth

Pagentを使って認証する:チェックをいれる
"keybord-interactive"認証を試みる:チェックをいれる
エージェントフォワーディングを認める:チェックをいれる

SSH=>トンネル
Putty_devel_tunnel

源ポート:20022
送り先:svn
ローカル:チェックをいれる
「追加」ボタンを押して、フォワードするポート一覧に「L20022 svn:22」と追加

セッションの項目に戻って「保存」ボタンを押します。
「開く」ボタンを押してログイン(パスワード認証ダイアログが出たら、鍵生成時のパスフレーズを入力)。
ログインできたら、開いたターミナルはそのままにしておきます。
次にSVNのSSHログイン設定をおこないます。
上記のトンネル経由でログインできるようにします。
putty.exeをダブルクリック、「PuTTY設定」を以下のように設定
セッション
Putty_svn_session

ホスト名:localhost
ポート:20022
接続タイプ:SSH
セッション一覧:任意の名前(例:svn)

接続=>データ
Putty_svn_data

自動ログインのユーザ名:repos

SSH=>認証

Pagentを使って認証する:チェックをいれる

セッションの項目に戻って「保存」ボタンを押します。
「開く」ボタンを押してログイン(パスワード認証ダイアログが出たら、鍵生成時のパスフレーズを入力)。
ログインできたら成功です。
TortoiseSVNの設定
適当な「作業フォルダ」の下で右クリックしてコンテキストメニューを開き、TortoiseSVN→「ここにリポジトリを作成」を選択します。
再び右クリックからTortoiseSVN→「設定」で、「一般」欄の「編集」ボタンを押下。設定ファイルがテキストで開くので、

[tunnels]
testssh(SVNトンネル用の適当なスキーマ名) = “<PuTTYのインストール先フォルダのパス>/plink.exe” -agent -load svn(PuTTYのセッション名) -i “<秘密鍵を保存したフォルダのパス>/id_rsa.ppk”

として保存。
例) testssh = “E:/tools/PuTTY/plinkw.exe” -agent -load svn -i “E:/tools/PuTTY/outer/id_rsa.ppk”
再び右クリックからTortoiseSVN→「SVNチェックアウト」で、

リポジトリのURL:svn+testssh:///usr/local/repos/project/trunk
チェックアウトDIR:<作業フォルダのパス>\project

として「OK」押下。これでチェックアウト完了です。
失敗する場合は設定ファイルを

[tunnels]
donassh(SVNトンネル用の適当なスキーマ名) = “<PuTTYのインストール先フォルダのパス>/plinkw.exe” -v -agent -load svn(PuTTYのセッション名) -i “<id_rsa.ppkを保存したフォルダのパス>/id_rsa.ppk”

として、再び「SVNチェックアウト」をおこないます。plinkw.exeはplink.exeのGUI版で、-v(verbose)をつけることでデバグコンソールから内容を確認できます。
これですべての設定が完了です。
外部開発者にアカウント名とパスワード、秘密鍵とパスフレーズを渡して、PuTTYとTortoiseSVNの設定をしてもらえば今回の用件を満たした開発がおこなえます。
■補足
ちなみにTrac「redmine」を使うと上で書きましたが、ついでにredmine側の設定で、SVN連携に必要な部分も書いておきます。
・開発サーバのredmineURL
設定=>リポジトリ
Redmine_repos_2

バージョン管理システム:Subversion
URL:http://svn/repos/project

として保存。プロジェクトの「リポジトリ」をクリックすると「リポジトリに、エントリ/リビジョンが存在しません」となる場合は開発サーバのSVNコマンドのパスがredmineに見えてないことが多いようなので、以下を修正

[inner@devel redmine]$ which svn
/usr/local/bin/svn
[inner@devel redmine]$ vi scm/adapters/subversion_adapter.rb
#SVN_BIN = "svn"
SVN_BIN = "/usr/local/bin/svn"

ついでにruby ver1.9.1でredmine ver1.0.3を動かす場合は以下を修正

[inner@devel redmine]$ vi vendor/rails/actionpack/lib/action_controller/request.rb
#value.each { |k, v|h[k] = normalize_parameters(v) }
value.each { |k, v|
v.force_encoding("UTF-8") if v.class==String
h[k] = normalize_parameters(v)
}