社内gitサーバで、kerberosベースのシングルサインオンを実現する

いっそ SaaS とかに移行したいところだが、社内規定と先立つものが……。

# 要点

* Active Directory環境でパスワード不要のgitサーバを立てました。
* apache で spnego認証ならmod_auth_gssapi が推奨らしい。
* docker 使いました。ソースは [github](https://github.com/tohosaku/git-server-gssapi) にアップしてあります [^1]

# きっかけ

　今まで社内gitサーバの認証には、BASIC認証 + LDAP(Active Directory)を使用していました。Windowsクライアントでgitサーバに接続するとID/パスワードは「資格情報マネージャ」に保存されます。

　一度保存してしまえばID/パスワードを聞かれることはないのですが、社内のポリシーではパスワードの定期変更が必要です。
　パスワード相違のときは新しいパスワード入力を促すという仕組みなら、それほどストレスは感じなかったでしょう。しかし現状ではgitは認証エラーが発生したら、新しいID/パスワードを聞くこともなく終了してしまいます。

**パスワード変更の都度、資格情報マネージャから認証情報を削除しないといけない。**

**めんどくさい。** 良い感じにシングルサインオンできないだろうかといろいろ調べました。[^2]

# 環境

## インフラ関連

|役割|バージョン|
|:-----------|:------------|
|ドメインコントローラー | Windows Server 2019 Standard|
|gitサーバ              | docker desktop 2.4.1.0|
|クライアント           | Windows 10 Pro 1909|
|gitクライアント        | git for windows 2.29.2.windows.3|

git for windows ではインストール時に認証を補助する「資格情報ヘルパー」を選択します。git 2.29 では [Git Credential Manager Core](https://github.com/microsoft/Git-Credential-Manager-Core) がデフォルトになっているので今回はこれで確認しました。

## Active Directory関連

|項目||
|:-----------|:------------|
|Active Directory ドメイン | hoge.local|
|ドメインコントローラ | pdc.hoge.local|
|レルム              | HOGE.LOCAL|
|チケット検証ユーザ | krbuser01|
|gitサーバ名 | gitsrv.hoge.local|
|gitを使用するユーザ| user01|

チケット検証ユーザは、ドメイン管理者に作ってもらいます。ただのドメインユーザです(gitサーバではなく、Active Directoryに作ります)。

# Apache で spnego認証を実現するまで

## そもそも spnego認証とは何か?

> SPNEGO (読み方:スプネゴ) とは Simple and Protected gss-api NEGOtiation mechanism の略で、Microsoft が作成した SSO のためのプロトコルです。GSS-API の上で動作します。
> GSS-API は (http, ldap, smtp, pop3, imap4 等の認証が必要な) 通信プロトコルの種類に依存せず、全ての通信プロトコル間で SSO を実現するためのフレームワークです。GSS-API では枠組みだけですので具体的な実装が必要です。その実装の 1 つが SPNEGO なのです。

[【図解】初心者にも分かるKerberos認証とspnegoの仕組み ～SSOのシーケンス,統合windows認証について～](https://milestone-of-se.nesuke.com/sv-advanced/activedirectory/kerberos-spnego/) より。

今回の構築、このページにお世話になりました。GSS-APIの実装で、HTTPに特化したのが spnego (という認識で良いんですよね？)

spnego認証の設定は「 [ApacheでActive Directory SSO](https://qiita.com/HishiM/items/41794fcba62e3924e5a2) 」を参考にしましたが、別のサイトでapacheのモジュールとしては、記事中で使用されている mod_auth_kerb ではなく mod_auth_gssapi を推奨していました。 [Apache2-mod_auth_kerb Is Dead, Use Mod_auth_gssapi](http://www.jfcarter.net/~jimc/documents/bugfix/41-auth-kerb.html) だそうです。

mod_auth_kerb 本家のメンテナンスはだいぶ前に止まっているらしい(各ディストリビューションではメンテされていると思いますが)のに比べ mod_auth_gssapi は

* [github](https://github.com/gssapi/mod_auth_gssapi) で開発されていて設定値の説明がわかりやすい。
* 開発も活発な模様。
* debian(buster)でも [パッケージ化](https://packages.debian.org/buster/libapache2-mod-auth-gssapi) されている。

と良さそうだったのでこちらを使うことにしました。

## Active Directory上の準備

* gitサーバにから Active Directory に問い合わせるためのユーザが必要です。ドメイン管理者にユーザを作成してもらってください。
* 作成されたユーザ名でkeytabファイルを作成します。このファイルは gitサーバで使用します。

### ユーザの設定

```
Set-ADUser krbuser01 -KerberosEncryptionType AES256,AES128,RC4

# 内容確認
Get-AdUser krbuser01 -property MsDS-SupportedEncryptionTypes
```

### keytab作成

```
ktpass -princ HTTP/gitsrv.hoge.local@HOGE.LOCAL -mapuser krbuser01@hoge.local -pass xxxxxx -crypto All -ptype KRB5_NT_PRINCIPAL -out kerberos.keytab  2> log

# 内容確認
Get-ADUser krbuser01 -property msDS-KeyVersionNumber

# 内容確認
dsquery * "CN=krbuser01,DC=hoge,DC=local" -attr msDS-KeyVersionNumber

# 内容確認
setspn -q 'HTTP/*' krbuser01
```

### 検証(gitサーバ構築後)

* gitサーバ構築後、以下のような形で keytabファイルの挙動を確認できます。(Windows上で確認する方法がわからない……)

```
$klist -ke -t /etc/kerberos.keytab

Keytab name: FILE:/etc/kerberos.keytab
KVNO Timestamp         Principal
---- ----------------- --------------------------------------------------------
  23 01/01/70 00:00:00 HTTP/gitsrv.hoge.local@HOGE.LOCAL (des-cbc-crc)
  23 01/01/70 00:00:00 HTTP/gitsrv.hoge.local@HOGE.LOCAL (des-cbc-md5)
  23 01/01/70 00:00:00 HTTP/gitsrv.hoge.local@HOGE.LOCAL (arcfour-hmac)
  23 01/01/70 00:00:00 HTTP/gitsrv.hoge.local@HOGE.LOCAL (aes256-cts-hmac-sha1-96)
  23 01/01/70 00:00:00 HTTP/gitsrv.hoge.local@HOGE.LOCAL (aes128-cts-hmac-sha1-96)

$ kinit -V -k -t /etc/kerberos.keytab HTTP/gitsrv.hoge.local

Using default cache: /tmp/krb5cc_0
Using principal: HTTP/gitsrv.hoge.local@HOGE.LOCAL
Using keytab: /etc/krb5.keytab
Authenticated to Kerberos v5
```

[参考](https://docs.informatica.com/ja_jp/big-data-management/big-data-management/10-0-update-1/_security-guide_big-data-management_10-0-update-1_ditamap/GUID-3FD16292-ECE3-45A1-8186-1DFB73201D8C/GUID-0BC449C2-4EC7-4F9B-8299-0C745C93638B/GUID-139A51E6-3E25-4889-B58B-B5F21F23511E/GUID-1EB20315-E001-4972-9A8E-2C2043A2CC94.html)

## Git サーバの設定

### kerberos の設定

* krb5.conf に Active Directory のドメイン構成を記載します。

```
[libdefaults]
  default_realm = HOGE.LOCAL

# The following krb5.conf variables are only for MIT Kerberos.
  kdc_timesync = 1
  ccache_type = 4
  forwardable = true
  proxiable = true

  fcc-mit-ticketflags = true

[realms]
HOGE.LOCAL = {
  kdc = pdc.hoge.local
  admin_server = pdc.hoge.local
  default_domain = hoge.local
}

[domain_realm]
.hoge.local = HOGE.LOCAL
hoge.local = HOGE.LOCAL

```

### apache の設定

git サーバとしてとりあえず、 git-http-backend を使用します。

```
ServerName gitsrv.hoge.local:80

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/

    SetEnv GIT_PROJECT_ROOT /var/lib/git/
    SetEnv GIT_HTTP_EXPORT_ALL
    ScriptAlias /git /usr/lib/git-core/git-http-backend

    RewriteEngine On
    RewriteCond %{QUERY_STRING} service=git-receive-pack [OR]
    RewriteCond %{REQUEST_URI} /git-receive-pack$
    RewriteRule ^/git/ - [E=AUTHREQUIRED]

    <Files "git-http-backend">
      AuthType GSSAPI
      AuthName "GSSAPI Auth"
      GssapiAllowedMech krb5
      GssapiCredStore keytab:/etc/kerberos.keytab

      Require valid-user
    </Files>

    <Location /gssapi-test>
      AuthType GSSAPI
      AuthName "GSSAPI Auth"
      GssapiAllowedMech krb5
      GssapiNegotiateOnce On
      GssapiCredStore keytab:/etc/kerberos.keytab
      Require valid-user

      AllowOverride None
      AddHandler cgi-script .cgi
      Options ExecCGI MultiViews SymLinksIfOwnerMatch
    </Location>

    ErrorLog /opt/log/error.log
    CustomLog /opt/log/access.log combined
    LogLevel Debug
</VirtualHost>
```

問題が発生したときの切り分けのため、ブラウザで認証の状況が確認できるように CGIスクリプトを用意しました。

```perl
#!/usr/bin/perl

print "Content-type: text/html\n\n";
print "<table>\n";

for $key (sort(keys(%ENV))) {
    print "<tr><td>$key</td><td>$ENV{$key}</td></tr>\n";
}

print "</table>\n";
exit;
```

## ブラウザで接続テスト

クライアントのチケットを全て削除して接続。

```
PS > klist purge

Current LogonId is 0:0x115ad2d
        Deleting all tickets:
        Ticket(s) purged!
```

ブラウザで http://gitsrv/gssapi-test/env.cgi にアクセスして画面にズラズラと環境変数が表示されれば成功です。

```
AUTH_TYPE	Negotiate
CONTEXT_DOCUMENT_ROOT	/var/www/
CONTEXT_PREFIX	
DOCUMENT_ROOT	/var/www/
GATEWAY_INTERFACE	CGI/1.1

中略

REMOTE_USER	user1@HOGE.LOCAL
REQUEST_METHOD	GET

後略

```

認証の成功を確認した後、クライアントでチケットを確認してみましょう。

```
PS> klist

Current LogonId is 0:0x115ad2d

Cached Tickets: (2)

#0>     Client: user1 @ HOGE.LOCAL
        Server: krbtgt/HOGE.LOCAL @ HOGE.LOCAL
        KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
        Ticket Flags 0x40e10000 -> forwardable renewable initial pre_authent name_canonicalize
        Start Time: 12/17/2020 15:37:43 (local)
        End Time:   12/18/2020 1:37:43 (local)
        Renew Time: 12/24/2020 15:37:43 (local)
        Session Key Type: AES-256-CTS-HMAC-SHA1-96
        Cache Flags: 0x1 -> PRIMARY
        Kdc Called: pdc.hoge.local

#1>     Client: user1 @ HOGE.LOCAL
        Server: HTTP/gitsrv.hoge.local @ HOGE.LOCAL
        KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
        Ticket Flags 0x40a10000 -> forwardable renewable pre_authent name_canonicalize
        Start Time: 12/17/2020 15:37:43 (local)
        End Time:   12/18/2020 1:37:43 (local)
        Renew Time: 12/24/2020 15:37:43 (local)
        Session Key Type: AES-256-CTS-HMAC-SHA1-96
        Cache Flags: 0
        Kdc Called: pdc.hoge.local
```

きちんとチケットが取得できています。

## そしてgitで接続テスト

### gitサーバ側の準備

一つ、空のBareリポジトリを作成しておきます。

```
$ mkdir fuga.git
$ cd fuga.git
$ git init --bare
```

### git クライアントの準備

git の設定として以下の設定をしておく必用があるようです

```
git config http.emptyAuth true
```

### いよいよ clone 

```
PS> git clone http://gitsrv.hoge.local/git/fuga.git
Cloning into 'fuga'...
warning: You appear to have cloned an empty repository.
```

できました!! (空のリポジトリをクローンしているので警告がでています)

# ハマったこと

　最初、ブラウザの接続は成功するけど、git clone は失敗する、という問題でハマりました。原因は git の URL指定時に、http://gitsrv/git/fuga.git とだけ書いて hoge.local を抜かしていたのが原因でした。

　ブラウザでの確認のときは、 http://gitsrv/gssapi-test/env.cgi で接続が成功していたのですが、gitはそうした接続には対応していない模様。

　当初は原因が分からず頭を抱えていましたが、接続後に `klist` コマンドで出力されるチケットの `Server` の項を確認して問題に気づきました。

# その他

git kerberos 等で検索していると、git clone 時に URLに `:@` を挟め (今回の例でいくと http://:@gitsrv/git/fuga.git) ということが書かれたりしていますが、今は不要なようです。

# 未確認事項

gitサーバが動いているホスト上でログインしようとすると kerberos認証ができず、basic認証のプロンプトが表示されてしまいます。他のクライアントから接続する場合は問題ありません。日常の使用に問題がないのであまり深追いしていませんが、kerberosの仕様かなにかでしょうか。もしご存じの方がいらっしゃれば教えて下さい。

[^1]: 設定に [mitamae](https://github.com/itamae-kitchen/mitamae) を使用しているのでアーキテクチャはamd64限定です。
[^2]: パスワードの定期変更を止めてもらうという社内政治はもっとめんどくさそうなので断念