torutkのブログ

ソフトウェア・エンジニアのブログ

CentOS 7.1にgitサーバーをapacheで立ててRedmine認証しようとして嵌ったこと

CentOS 6にapache経由でhttpでアクセスできるgitサーバーを立てて、Redmine認証をする方法は以前に確立していました。
http://d.hatena.ne.jp/torutk/20130117/p1

今回、CentOS 7に同じ構成でgitサーバーを立ててRedmine認証をさせようとしたところ、大いにはまってしまいました。はまった事柄をひたすら本日の日記に書きました。結論だけ知りたい方は以下を参照ください。
CentOS 7でapache経由のgitサーバーを構築 - ソフトウェアエンジニアリング - Torutk

httpdApache)をインストール

最初に、httpdをインストールし、ポート番号をデフォルトの80から8008に変更し、httpdの起動および自動起動設定までを行います。

yumhttpdパッケージをインストール
~$ sudo yum install httpd

CentOS 7.1は標準でhttpd 2.4.6*1を搭載しています。

httpdのポート番号を変更

今回は、RedmineがNginxサーバー経由で稼動しポート80を使用しているマシンに立てるので、Apache は別なポート番号を使用する必要がありました。

新たなポート番号の候補は、IANAで公式登録されている中から、サービス名http-altで登録されているポート番号8008もしくは8080、または登録がない8009-8018です。

ここでは、SELinuxでhttp_port_t(httpdプロセスに付与されるタグ)がデフォルトでアクセス可能なポート番号である8008を使います。

ポート番号の設定は、/etc/httpd/conf/httpd.confに定義します。以下修正をします。

Listen 8008
httpdが使用するポート番号wファイアウォールで許可

ファイアウォールで8008を許可します。

~$ sudo firewall-cmd --permanent --add-port=8008/tcp
success
~$ sudo firewall-cmd --reload
success
~$
  • 8008は/etc/servicesにはデフォルトで登録されていないので、ポート番号で指定します。
httpdの起動確認

httpdの起動

~$ sudo systemctl start httpd
~$ sudo systemctl status httpd
httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled)
   Active: active (running) since 日 2015-11-22 12:26:15 JST; 3s ago
 Main PID: 11549 (httpd)
   Status: "Processing requests..."
   CGroup: /system.slice/httpd.service
           ├11549 /usr/sbin/httpd -DFOREGROUND
           ├11550 /usr/sbin/httpd -DFOREGROUND
           ├11551 /usr/sbin/httpd -DFOREGROUND
           ├11552 /usr/sbin/httpd -DFOREGROUND
           ├11553 /usr/sbin/httpd -DFOREGROUND
           └11554 /usr/sbin/httpd -DFOREGROUND

11月 22 12:26:15 ravello httpd[11549]: AH00558: httpd: Could not reliably d...e
11月 22 12:26:15 ravello systemd[1]: Started The Apache HTTP Server.
Hint: Some lines were ellipsized, use -l to show in full.
~$
  • 注)AH000558の警告は、httpd.conf のServerNameを正しく設定すれば解消します。

Webブラウザで、このマシンのポート8008にアクセスします。「Testing 123..」というページが表示されればhttpdの最低限の設定はOKです。

httpd自動起動設定

設定がOKであれば、OS起動時に自動でhttpdが起動するように設定します。

~$  sudo systemctl enable httpd
ln -s '/usr/lib/systemd/system/httpd.service' '/etc/systemd/system/multi-user.target.wants/httpd.service'
~$

Gitのリポジトリを作成

CentOS 7.1は標準でgit 1.8.3.1を搭載しています。
まず、/var/lib/git ディレクトリを設け、その下に動作確認用のgitリポジトリ(ベア)を作成します。

~$ sud mkdir /var/lib/git
~$ sudo git init --bare --shared /var/lib/git/redmine-testing.git
~$ cd /var/lib/git/redmine-testing.git
redmine-testing.git$  sudo git update-server-info
redmine-testing.git$  sudo mv hooks/post-update.sample hooks/post-update
redmine-testing.git$  sudo chown -R apache.apache /var/lib/git

Gitリポジトリhttpd経由でアクセスする(認証なし)

Gitリポジトリhttpdで参照するように設定を記述します。
まずは認証無しの設定を記述し動作確認をします。

/etc/httpd/conf.d/git-redmine.conf


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

httpdに設定変更を反映します。

~$ sudo systemctl reload httpd
つまづき(その1)

Webブラウザでgitディレクトリ以下にアクセスすると、設定前は404エラーだったのが403エラーに変化します。/var/log/httpd/error_log を参照すると次のエラーメッセージが記録されています。

[Sun Nov 22 13:06:29.166344 2015] [authz_core:error] [pid 12010]
 [client 192.168.123.138:56350] AH01630: client denied by server configuration:
 /usr/libexec/git-core/git-http-backend

Apache 2.4では、明示的にRequireが指定されていないディレクトリにはアクセスができなくなっています。
/etc/httpd/conf.d/git-redmine.conf に以下を追記します。

<LocationMatch "^/git/">
        Require all granted
</LocationMatch>
  • これは最低限の動作確認用の記述なので、後で認証済みのユーザーのみがアクセス可能となるように書き換えます。
つまづき(その2)

つまづき(その1)の対処を実施したあと、gitのcloneはできるようになりましたが、pushが403エラーとなります。/var/log/htttpd/error_logには次の記録があります。

[Sun Nov 22 15:23:25.675742 2015] [cgi:error] [pid 12087] [client 192.168.123.138:59060] AH01215: Service not enabled: 'receive-pack'

git-http-backend CGIは、環境変数REMOTE_USERが未定義だとユーザー認証が失敗したとみなして書き込み(push)を拒否するので、定義を追加します。

/etc/http/conf.d/git-redmine.conf に以下を追記します。

SetEnv REMOTE_USER $REDIRECT_REMOTE_USER
  • Basic認証をしていると、REMOTE_USERが定義されるようです。今回のように検証のためBasic認証を外したので問題に嵌ったようです。

Gitリポジトリhttpd経由でアクセスする(認証あり)

Redmineの認証を利用してGitの(Apacheの)認証を行います。
Redmineに含まれるRedmine.pm(mod_perlモジュール)を使って認証するために、httpdからperlを利用します。

/var/lib/redmine/extra/svn/Redmine.pm へのシンボリックリンクPerlのモジュール検索パスのいずれかに作成します。ここでは、/etc/httpd の下に Apache/Authn/Redmine.pm となるように設けます。

~$ sudo mkdir -p /etc/httpd/Apache/Authn
~$ sudo ln -s /var/lib/redmine/extra/svn/Redmine.pm /etc/httpd/Apache/Authn/Redmine.pm

/etc/httpd/conf.d/git-redmine.confにいろいろ追記します。次はgit-redmine.confの全体像です。

PerlLoadModule Apache::Authn::Redmine

SetEnv GIT_PROJECT_ROOT /var/lib/git
SetEnv GIT_HTTP_EXPORT_ALL
SetEnv REMOTE_USER $REDIRECT_REMOTE_USER

ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/

<LocationMatch "^/git/">
  PerlAccessHandler Apache::Authn::Redmine::access_handler
  PerlAuthenHandler Apache::Authn::Redmine::authen_handler
  AuthType Basic
  AuthName "Git Redmine"

  # for Redmine Authentication
  RedmineDSN "DBI:mysql:database=redmine;host=localhost"
  RedmineDbUser "redmine"
  RedmineDbPass "redmine"
  RedmineGitSmartHttp yes

  Require valid-user
</LocationMatch>
つまづき(その3)

CentOS 6/apache 2.2 では、Perlモジュールを使うために apachemod_perlをロードしていました。ところが、CentOS 7/apache 2.4には、mod_perlがありません。

CentOS 7(RHEL 7)からは、mod_perlは非推奨(Apache 2.4対応の点で)であり、替わりに mode_fcgid を推奨となっています。

引き続きmod_perlを使う必要があるならば、EPELにあるのでそれをインストールします。
EPELリポジトリを/etc/yum.conf.d/に設定していなければ、設定します。

~$ sudo yum install epel-release

これはCentOS 7のextrasリポジトリに含まれるパッケージで、EPELリポジトリを利用する設定をインストールします。

~$ sudo yum install mod_perl perl-Digest-SHA 
つまづき(その4)

mod_perlも入れて、さあGitにアクセスしてみると、今度は500エラーが発生します。

/var/log/httpd/access_logより

[22/Nov/2015:17:22:29 +0900] "GET /git/redmine-testing.git/info/refs?service=git-upload-pack HTTP/1.1" 500 527 "-" "git/2.5.3"

/var/log/httpd/error_logより

[Sun Nov 22 17:22:29.501030 2015] [core:error] [pid 12529] [client 192.168.123.138:62970] 
AH00027: No authentication done but request not allowed without authentication for 
/git/redmine-testing.git/info/refs. Authentication not configured?

ぐぐってみると、PerlAccessHandler がapache 2.4では振る舞いが変わってしまい正常に動作しないとのことです。

Redmine.orgのフォーラム・チケットに同じ問題が報告されていました(回答なし)

この問題に言及しているブログもいくつかありました。

ここでの回避策は、/etc/httpd/conf.d/git-redmine.confから、PerlAccessHandlerの設定を削除することがよさそうです。PerlAccessHandlerを削除すると、匿名で読み出し可能なRedmineプロジェクトのリポジトリであっても認証が要求されてしまいますが、認証は機能するようになります。

/etc/httpd/conf.d/git-redmine.conf

  :(前略)
<LocationMatch "^/git/">
  # PerlAccessHandler Apache::Authn::Redmine::access_handler
  PerlAuthenHandler Apache::Authn::Redmine::authen_handler
  :(後略)
Redmine.pmのハック

http://stackoverflow.com/questions/22638972/debug-apache-2-4-perlauthenhandler
この記事を見ながら、よく分からないperlをいじって次のように修正してみました。

sub access_handler {
  my $r = shift;

  unless ($r->some_auth_required) {
      $r->log_reason("No authentication has been configured");
      return FORBIDDEN;
  }

  return OK unless request_is_read_only($r);

  my $project_id = get_project_identifier($r);

  if (is_public_project($project_id, $r) && anonymous_allowed_to_browse_reposito
ry($project_id, $r)) {
      $r->set_handlers(PerlAuthenHandler => [\&OK]);
      $r->user("");
  }

  return OK;
}
  • 後半のif文を修正しています。処理としては、$r->user(""); の追加をしています。


2パターン(公開プロジェクトと非公開プロジェクト)で試してみた限りはうまくいってそうです。

Gitリポジトリhttpd経由でアクセスする(LDAP認証あり)

Redmine側でLDAP認証設定をしておきます。

/etc/httpd/conf.d/git-redmine.conf に1行追加します。

PerlLoadModule Authen::Simple::LDAP
perl-Authen-Simple-LDAPrpmパッケージを作る

追記したモジュールは、CentOS 7の標準パッケージおよびEPELには存在しないので、RPMパッケージを作成しておきます。作成方法と作成したRPMは次に記載、添付しています。

CentOS 6/7でPerl-Authen-Simple-LDAPのRPMパッケージを作る - ソフトウェアエンジニアリング - Torutk

つまづき(その5)

LDAPユーザーでgitにアクセスすると500エラーとなります。/var/log/httpd/error_logを見ると

[Sun Nov 22 22:13:25 2015] [error] [Authen::Simple::LDAP] Failed to connect to 
'ad.domain.local'. Reason: 'IO::Socket::INET: connect: Permission denied'

LDAPポート(389)へのアクセスでSELinuxエラーとなっています。/var/log/audit/audit.logでtype=AVCを検索すると

type=AVC msg=audit(1448198005.727:1769): avc:  denied  { name_connect } for  pid=13500 comm="/usr/sbin/httpd" dest=389 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:ldap_port_t:s0 tclass=tcp_socket

と出てきます。

httpdプロセスがポート番号389でTCPコネクションを張るには、httpd_t タイプに通信ポートのセキュリティコンテキスト設定で追加指定をします。

デフォルトでアクセスできるポートは、

~$ sudo semanage port -l |grep '^http_port_t'
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000

ここにポート389を追加します。

~$ sudo semanage port -a -t http_port_t -p tcp 389
ValueError: ポート tcp/389 はすでに定義されています

なんと・・・

いろいろ試行錯誤をしているうちに、このポートコンテキストはリッスン(そのマシンが通信を受ける際のポート番号)ではないかと思い当たりました。そして、389はldap_port_tに割り当たっています。

audit.logにあるエラーをかいつまんでどうしたらいいかを提示するツール sealert があるのでそれを使ってみます(setroubleshoot-serverパッケージに含まれる)。

~$ sudo sealert -a /var/log/audit/audit.log
 81% done'list' object has no attribute 'split'
100% done
found 4 alerts in /var/log/audit/audit.log
  :
SELinux is preventing /usr/sbin/httpd from name_connect access on the tcp_socket port 389.

*****  Plugin catchall_boolean (24.7 confidence) suggests   ******************

If httpd が LDAP ポートに接続することを許可します。 がしたい
Then 'httpd_can_connect_ldap' boolean を有効にすることにより、 これを SELinux に伝える必要があります。
詳細情報については、'httpd_selinux' man ページをご覧下さい。
Do
setsebool -P httpd_can_connect_ldap 1
  :

これを実行してみます。

~$ sudo setsebool -P httpd_can_connect_ldap 1
~$

無事pushできました。

つまづき(その6)

つまづき(その5)でsealertコマンドでSELinuxの監査ログから問題と対処のレポートを出しました。
この中で、LDAP接続とは別にgit関連のレポートが上がっていたので対応をしておきます。

SELinux is preventing /usr/libexec/git-core/git from execute access on the file post-update.

*****  Plugin catchall_labels (83.8 confidence) suggests   *******************

If you want to allow git to have execute access on the post-update file
Then post-update のラベルを変更する必要があります
Do
# semanage fcontext -a -t FILE_TYPE 'post-update'
この FILE_TYPE 以下のどれかです: NetworkManager_exec_t, abrt_dump_oops_exec_t, a
brt_exec_t, abrt_handle_event_exec_t, abrt_helper_exec_t, abrt_retrace_coredump_
  :(中略)
er_exec_t, zoneminder_script_exec_t, zos_remote_exec_t.
次にこれを実行してください:
restorecon -v 'post-update'

*****  Plugin catchall (17.1 confidence) suggests   **************************

If git に、 post-update file の execute アクセスがデフォルトで許可されるべきです。
Then バグとして報告してください。
ローカルのポリシーモジュールを生成すると、
 このアクセスを許可することができます。
Do
このアクセスを一時的に許可するには、以下を実行してください。
# grep git /var/log/audit/audit.log | audit2allow -M mypol
# semodule -i mypol.pp

Additional Information:
Source Context                system_u:system_r:httpd_t:s0
Target Context                unconfined_u:object_r:var_lib_t:s0
Target Objects                post-update [ file ]
Source                        git
Source Path                   /usr/libexec/git-core/git
Port                          <Unknown>
Host                          <Unknown>
Source RPM Packages           git-1.8.3.1-4.el7.x86_64
Target RPM Packages
Policy RPM                    selinux-policy-3.13.1-23.el7_1.21.noarch
Selinux Enabled               True
Policy Type                   targeted
Enforcing Mode                Enforcing
  :

まず、最初のメッセージ

SELinux is preventing /usr/libexec/git-core/git from execute access on the file post-update.

から、gitがフックスクリプトpost-updateを実行することができないことが分かります。

Additional Information:
Source Context                system_u:system_r:httpd_t:s0
Target Context                unconfined_u:object_r:var_lib_t:s0
Target Objects                post-update [ file ]

と、ここが問題かと思います。
post-updateスクリプトはgitリポジトリがある/var/lib/gitの下に置かれます。
/var/lib/gitに設定されているファイルコンテキストはvar_lib_tで、
そこをアクセスするgit(httpdプロセスとして)のコンテキストのタイプがhttpd_tとなっています。

sesearch でhttpd_tがvar_lib_tにアクセスできるか調べてみます。sesearchは、setools-consoleパッケージに含まれます。

~$ sesearch --allow -n -s httpd_t -t var_lib_t
    :
   allow httpd_t var_lib_t : file { ioctl read getattr lock open } ;
    :

と確かにexecuteはないですね。
httpdからアクセスすることを最初から想定している/var/www/htmlは、コンテキストがhttpd_sys_content_tとなっていますが、httpd_tからのファイルへのアクセスは同等です。

~$ sesearch --allow -n -s httpd_t -t httpd_sys_content_t
    :
   allow httpd_t httpd_sys_content_t : file { ioctl read getattr lock open } ;
    :

そこで、httpd_tを持つプロセスがファイルの実行を許すコンテキストを探してみると、httpd_sys_script_exect_tがありました。
そこで、gitリポジトリ配下のhooksディレクトリ以下には、httpd_sys_script_exec_tを設定します。

~$ sudo semanage fcontext -a -t httpd_sys_script_exec_t '/var/lib/git/[^/]+/hooks(/.*)?'
~$ sudo restorecon -v -R /var/lib/git
 restorecon reset /var/lib/git context unconfined_u:object_r:var_lib_t:s0->unconfined_u:object_r:git_sys_content_t:s0
restorecon reset /var/lib/git/redmine-testing.git context unconfined_u:object_r:var_lib_t:s0->unconfined_u:object_r:git_sys_content_t:s0
restorecon reset /var/lib/git/redmine-testing.git/refs context unconfined_u:object_r:var_lib_t:s0->unconfined_u:object_r:git_sys_content_t:s0
restorecon reset /var/lib/git/redmine-testing.git/refs/heads context unconfined_u:object_r:var_lib_t:s0->unconfined_u:object_r:git_sys_content_t:s0
  :

あれあれ、restoreconを実行したら、var_lib_tだったディレクトリ・ファイルがgit_sys_contentにどんどん書き換えられてしまいました。

~$ sudo semanage fcontext -l | grep "var/lib" | grep git
/var/lib/git(/.*)?                                 all files          system_u:object_r:git_sys_content_t:s0
/var/lib/git/[^/]+/hooks(/.*)?                     all files          system_u:object_r:httpd_sys_script_exec_t:s0
/var/lib/gitolite(3)?(/.*)?                        all files          system_u:object_r:gitosis_var_lib_t:s0
/var/lib/gitolite/\.ssh(/.*)?                      all files          system_u:object_r:ssh_home_t:s0
/var/lib/gitolite3/\.ssh(/.*)?                     all files          system_u:object_r:ssh_home_t:s0
/var/lib/gitosis(/.*)?                             all files          system_u:object_r:gitosis_var_lib_t:s0
~$

となっており、デフォルトでは/var/lib/git以下はgit_sys_content_tに初期設定されているのが分かります。
削除を試みたところ、

~$ sudo semanage fcontext -d "/var/lib/git(/.*)?"
ValueError: /var/lib/git(/.*)? のファイルコンテキストはポリシーで定義されていま す、 削除できません

と拒否されてしまいました。

修正できるかやってみます。

~$ sudo semanage fcontext -m -t var_lib_t "/var/lib/git(/.*)?"
libsemanage.dbase_llist_query: could not query record value
~$
||< 

だめなようです。

>||
~$ sudo semanage fcontext -a -t var_lib_t "/var/lib/git(/.*)?"
~$ sudo semanage fcontext -l | grep "/var/lib" |grep git
/var/lib/git(/.*)?                                 all files          system_u:object_r:var_lib_t:s0
/var/lib/git/[^/]+/hooks(/.*)?                     all files          system_u:object_r:httpd_sys_script_exec_t:s0

restorecondしてみると、今度はvar_lib_tになってしまいました。
semanage fcontextで設定した情報は、/etc/selinux/targeted/contexts/files/file_contexts.localに記録されます。

~$ cat /etc/selinux/targeted/contexts/files/file_contexts.local
# This file is auto-generated by libsemanage
# Do not edit directly.

/var/ftp/pub(/.*)?    system_u:object_r:public_content_rw_t:s0
/var/lib/git/[^/]+/hooks(/.*)?    system_u:object_r:httpd_sys_script_exec_t:s0
/var/lib/git(/.*)?    system_u:object_r:var_lib_t:s0
~$

と、順番が関係していそうです。

いったん、hooksの設定を削除してから新たに追加します。

~$ sudo semanage fcontext -d '/var/lib/git/[^/]+/hooks(/.*)?'
~$ cat /etc/selinux/targeted/contexts/files/file_contexts.local
# This file is auto-generated by libsemanage
# Do not edit directly.

/var/ftp/pub(/.*)?    system_u:object_r:public_content_rw_t:s0
/var/lib/git(/.*)?    system_u:object_r:var_lib_t:s0
~$ sudo semanage fcontext -a -t httpd_sys_script_exec_t '/var/lib/git/[^/]+/hooks(/.*)?'
~$ cat /etc/selinux/targeted/contexts/files/file_contexts.local
# This file is auto-generated by libsemanage
# Do not edit directly.

/var/ftp/pub(/.*)?    system_u:object_r:public_content_rw_t:s0
/var/lib/git(/.*)?    system_u:object_r:var_lib_t:s0
/var/lib/git/[^/]+/hooks(/.*)?    system_u:object_r:httpd_sys_script_exec_t:s0
~$

これでrestorecondしたら、こんどは大丈夫でした。

しかし、これでgit pushしてみるとSELinuxエラーが発生します。

[Mon Nov 23 01:32:00.409768 2015] [cgi:error] [pid 13499] [client 192.168.123.138:53301] AH01215: error: Couldn't set refs/heads/master

audit.logをsealerで解析すると

SELinux is preventing /usr/libexec/git-core/git from unlink access on the file master.

*****  Plugin catchall_labels (83.8 confidence) suggests   *******************

If you want to allow git to have unlink access on the master file
Then master のラベルを変更する必要があります
Do
# semanage fcontext -a -t FILE_TYPE 'master'
この FILE_TYPE 以下のどれかです: abrt_retrace_spool_t, apcupsd_cgi_rw_content_t,
 awstats_rw_content_t, bugzilla_rw_content_t, collectd_rw_content_t, cvs_rw_content_t,
 dirsrv_config_t, dirsrv_var_log_t, dirsrv_var_run_t, dirsrvadmin_config_t,
 dirsrvadmin_rw_content_t, dirsrvadmin_tmp_t, dspam_rw_content_t, git_rw_content_t,
 httpd_cache_t, httpd_lock_t, httpd_squirrelmail_t, httpd_sys_rw_content_t,
 httpd_tmp_t, httpd_tmpfs_t, httpd_user_rw_content_t, httpd_var_lib_t, httpd_var_run_t,
 jetty_cache_t, jetty_log_t, jetty_var_lib_t, jetty_var_run_t, keystone_cgi_rw_content_t,
 krb5_host_rcache_t, man2html_rw_content_t, mediawiki_rw_content_t, mediawiki_tmp_t,
 mirrormanager_var_run_t, mojomojo_rw_content_t, munin_rw_content_t, mythtv_rw_content_t,
 nagios_rw_content_t, nutups_cgi_rw_content_t, openshift_rw_content_t, passenger_tmp_t,
 passenger_var_run_t, pki_ra_etc_rw_t, pki_ra_log_t, pki_ra_var_lib_t, pki_tps_etc_rw_t,
 pki_tps_log_t, pki_tps_var_lib_t, prewikka_rw_content_t, smokeping_cgi_rw_content_t,
 squid_rw_content_t, squirrelmail_spool_t, systemd_passwd_var_run_t,
 w3c_validator_rw_content_t, webalizer_rw_content_t, zarafa_var_lib_t,
 zoneminder_rw_content_t, zoneminder_var_lib_t.
次にこれを実行してください:
restorecon -v 'master'


*****  Plugin catchall (17.1 confidence) suggests   **************************

If git に、 master file の unlink アクセスがデフォルトで許可されるべきです。
Then バグとして報告してください。
ローカルのポリシーモジュールを生成すると、
 このアクセスを許可することができます。
Do
このアクセスを一時的に許可するには、以下を実行してください。
# grep git /var/log/audit/audit.log | audit2allow -M mypol
# semodule -i mypol.pp


Additional Information:
Source Context                system_u:system_r:httpd_t:s0
Target Context                system_u:object_r:var_lib_t:s0
Target Objects                master [ file ]

  :

/var/lib/git以下は、var_lib_tでは弱かったようです。
httpd_var_lib_t を使うことにします。

~$ sudo semanage fcontext -d "/var/lib/git/[^/]+/hooks(/.*)?"
~$ sudo semanage fcontext -d "/var/lib/git(/.*)?"
~$ sudo semanage fcontext -a -t httpd_var_lib_t '/var/lib/git(/.*)?'
~$ sudo semanage fcontext -a -t httpd_sys_script_exec_t '/var/lib/git/[^/]+/hooks(/.*)?'
~$ sudo restorecon -v -R /var/lib/git

これで良い感じです。

*1:CentOS 6ではhttpd 2.2でした。この2.2⇒2.4への変更が今回の問題