CentOS 6にapache経由でhttpでアクセスできるgitサーバーを立てて、Redmine認証をする方法は以前に確立していました。
http://d.hatena.ne.jp/torutk/20130117/p1
今回、CentOS 7に同じ構成でgitサーバーを立ててRedmine認証をさせようとしたところ、大いにはまってしまいました。はまった事柄をひたすら本日の日記に書きました。結論だけ知りたい方は以下を参照ください。
CentOS 7でapache経由のgitサーバーを構築 - ソフトウェアエンジニアリング - Torutk
httpd(Apache)をインストール
最初に、httpdをインストールし、ポート番号をデフォルトの80から8008に変更し、httpdの起動および自動起動設定までを行います。
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です。
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
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モジュールを使うために apacheにmod_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エラーが発生します。
[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のフォーラム・チケットに同じ問題が報告されていました(回答なし)
この問題に言及しているブログもいくつかありました。
- SubversionのApache経由アクセスの設定 - Memo
- SubversionByRedmine · glad2121/bitnami-redmine-ext Wiki · GitHub
- perl - Debug Apache 2.4 PerlAuthenHandler - Stack Overflow
ここでの回避策は、/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 :(後略)
- PerlAccessHandlerの行をコメントアウト
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認証あり)
/etc/httpd/conf.d/git-redmine.conf に1行追加します。
PerlLoadModule Authen::Simple::LDAP
perl-Authen-Simple-LDAPのrpmパッケージを作る
追記したモジュールは、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
これで良い感じです。