LDAP で gitolite / gitosis  のユーザを管理する

# 背景
### gitolite って?
- [gitolite](http://gitolite.com/gitolite/) : ユーザごとのアクセス権管理などなどができる git hosting ツール
- gitosis : gitolite よりちょっと古いやつ、似たようなもの

### ユーザ管理はどんな仕組み?
http://qiita.com/yuku_t/items/46d1d834fbdae271d66c の記事が大変わかり易いです。

gitolite / gitosis は SSH authorized_keys の `command=xx` 機能を使っており、
gitolite 自身が持つ管理リポジトリ内に `gitolite-admin.git/keydir/username.pub` といったユーザ名の公開鍵を配置して push することで、hosting ユーザの authorized_keys に各ユーザの公開鍵が識別可能な形で追記される。

gitosis もこの辺は同様なので以下 gitosis については省略。

# やりたいこと
- LDAP サーバによって組織ユーザの公開鍵が集中管理されている状況において、その鍵とユーザ識別子を使って gitolite を使いたい。
- `gitolite-admin.git/keydir/` には LDAP で管理されているユーザの鍵をダブって管理させたくない。
- LDAP サーバに無いユーザが使うケースを考慮し、従来通り `keydir` に入れたものも併用できるようにしておきたい。

# 前提
## LDAP 上のデータ
LDAP サーバからユーザ識別子と公開鍵 (それぞれ `uid`, `sshPublicKey` という属性名とする) が取得できること。

```
$ ldapsearch ${MY_QUERY} -o ldif-wrap=no -LLL 'uid' 'sshPublidKey' | my_filter_cmd
 uid: user1-name
 sshPublicKey: ssh-rsa AAAAxxxxxxxxxxxxx...

 uid: user2-name
 sshPublicKey: ssh-rsa AAAAyyyyyyyyyyyyy...

 uid: user3-name
 sshPublicKey: ssh-rsa AAAAzzzzzzzzzzzzz...
 
```

のような出力が得られるものとする。

## gitolite 設定
hosting ユーザは `git` とする。
`$ git clone git@gitolite-server.com:myrepository.git`
のようにして使える状態。

gitolite コマンドユーティリティは `/home/git/bin` にインストールされ、setup により下記のような `authorized_keys` を生成しているものとする。

```:/home/git/.ssh/authorized_keys
# gitolite start
command="/home/git/bin/gitolite-shell admin",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAxxxxxxxxxxxyyyyyyyyyyyyyyyzzzzzzzzzzzzzz admin@somehost
command="/home/git/bin/gitolite-shell localuser",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAaaaaaaaaaaaaaabbbbbbbbbbbbbbbbcccccccc localuser@somehost
...
# gitolite end
```

# 実現方法
## 案1. LDAP 参照用の authorized_keys を分ける (汎用)
従来の方法で `git` ユーザが参照する `$HOME/.ssh/authorized_keys` はそのままにし、LDAP からの情報をセカンダリで参照するファイルに持っておく。

```conf:/etc/ssh/sshd_config
AuthorizedKeysFile      %h/.ssh/authorized_keys %h/.ssh/ldap_authorized_keys
```

`/usr/local/bin/gitolite-ssh-ldap` のような LDAP のデータを上手いこと整形するスクリプトを作成し、以下のような出力が得られるようにする。

```shell-session
$ /usr/local/bin/gitolite-ssh-ldap gitolite
command="/home/git/bin/gitolite-shell user1-name", no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAxxxxxxxxxxxxx... user1-name@somehost
command="/home/git/bin/gitolite-shell user2-name", no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAyyyyyyyyyyyyy... user2-name@somehost
command="/home/git/bin/gitolite-shell user3-name", no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAzzzzzzzzzzzzz... user3-name@somehost
...
...
```

上記の結果を定期的に拾うなどしてセカンダリの authorized_keys ファイルに書き込む。

```cron:/etc/cron.d/sync_ldap_gitolite
*/30 * * * * git /usr/local/bin/gitolite-ssh-ldap gitolite > /home/git/.ssh/ldap_authorized_keys
```

これでOK。sshd が両方を参照して認証を行うので `gitolite-admin.git/keydir` から生成された既存の鍵も変更無しで使用可能。他の一般ユーザに対しても、 `$HOME/.ssh/ldap_authorized_keys` が無ければ何も起きないので多くの場合は競合しないと思われる。

この方法では定期的にデータを拾っているため、LDAP サーバ上のデータ反映に多少の時間差が発生する。

## 案2. sshd の AuthorizedKeysCommand を使う (要 openssh-server >= 6.2)
### このオプションについて
`AuthorizedKeysCommand` を使うと、SSH 認証時に実行する任意のコマンドを指定できる。ここに指定したコマンドにはログインユーザ名が渡され、そのユーザの公開鍵が標準出力されることが期待される。例えば下記のような挙動をするコマンドを指定する。

```shell-session
$ my_pubkey_cmd user-name 
ssh-rsa AAAAaqwsedrftgyhujiko.... user-name@somehost
```

openssh-server >= 6.2 以降で有効になっている。Ubuntu の場合は 13.10 から公式パッケージが配布されている。

### スクリプト準備
hosting のユーザ名が渡ってきた場合のみ異なる挙動をするコマンドを作成する。

前述の上手いこと整形するスクリプト `/usr/local/bin/gitolite-ssh-ldap` もあるものとすると、下記のように引数で渡されたユーザで出力を分岐させる。

```shell-session:/usr/local/bin/gitolite-helper
#!/bin/sh
...
...

case $1 in
  git)
    # gitolite 用に LDAP 上の複数ユーザの鍵を command="/home/git/bin/gitolite-shell を含んだ形で出力
    /usr/local/bin/gitolite-ssh-ldap gitolite
    ;;
  *)
    # 普通に個人の鍵を出力
    /path/my_pubkey_cmd $1
    ;;
esac
```

### sshd 設定
先ほどのコマンドを指定することで gitolite のユーザ認証と連携する。

```conf:/etc/ssh/sshd_config
AuthorizedKeysCommand      /usr/local/bin/gitolite-helper
AuthorizedKeysCommandUser  git 
```

これで OK。まず `AuthorizedKeysCommand` で認証が試みられ、その中にマッチする鍵がなければ `AuthorizedKeysFile` などの次の認証にうつるため既存の方法も併用できる。

SSH 毎に LDAP サーバに取りにいけるためリアルタイムで反映できるが、ユーザ数が増えると重くなりやすいのと、前の方法よりも sshd への影響が大きいので他のサービスと共存しているような場合にあまり向かない。