先日の続きです。
torutk.hatenablog.jp
SELinux下でlogrotateを使ってRedmineのログ・ローテーションを実現するには設定がそれなりに手間なので、Railsの機能を使ってログローテーションを設定します。
Redmineインストールディレクトリのconfig/additional_environment.rb にログ設定を記述
config.logger = Logger.new('log/production.log', 30, 10 * 1024 * 1024)
- 一つ目の引数は、ログファイルのパスを指定します。Railsアプリケーションのルートディレクトリからの相対パスで指定可。
- 二つ目の引数は、ローテートするときに残す世代数(ファイル数)です。
- 三つ目の引数は、ログファイルの容量がこの閾値に達したらローテーションを実施する値です。
gitサーバー
このマシンに共有gitリポジトリを設けて、Redmineから参照し、またHTTP経由でリポジトリのクローンとプッシュをできるようにします。
HTTPからのアクセスにおいて、HTTP用にアカウントを作成し運用管理するのは負担が大きいですから、RedmineのアカウントでHTTPの認証を行うようにします。Redmineには現時点ではApache HTTPD用の認証モジュールが含まれていますがNginxには対応していません。
https://www.redmine.org/issues/7061
そこで、Git(Subversion)リポジトリアクセス用にApache HTTPDをインストールします。
gitリポジトリに対しては、読み込み、書き込み、およびスクリプトの実行が必要です。HTTP経由でgitリポジトリにアクセスするには、HTTPのプロセスに与えられるドメインhttpd_tから、gitリポジトリのリソースに対する読み込み(map操作含む)、書き込み、スクリプト実行の操作の許可があるリソースタイプをgitリポジトリに割当てます。
CentOS 8のデフォルトSELinux設定では、gitリポジトリを置くと想定しているディレクトリに対して次のリソースタイプが割当てられています。また、ディレクトリ内のファイルに対して許可される操作も併記します。調査コマンドについては後述します。
基点ディレクトリ |
リソースのタイプ |
ドメインhttpd_tからファイルへの許可された操作 |
/var/lib/git |
git_sys_content_t |
getattr ioctl lock map open read |
/var/www/git |
git_content_t |
getattr ioctl lock map open read |
これらのデフォルトSELinux設定では、書き込み操作およびスクリプトの実行操作が許可されていません。
そこで、書き込み操作と、必要なディレクトリに限定して実行操作を許可するリソースタイプを設定します。
- ドメインhttpd_tから/var/lib/git 以下のリソースに対して読み書き許可
- ドメインhttpd_tから/var/lib/git/<リポジトリ名>/hooks 以下のリソースに対して実行許可
ドメインhttpd_tから上述の許可を持つリソースタイプを調査したところ、次のタイプが適しています。
- git_rw_content_t
- git_script_exec_t
SELinuxのポリシー設定を変更します。
/var/lib/git/下のリポジトリディレクトリは、読み書きが可能なgit_rw_content_tを割当てます。デフォルトでルール定義があるので-mオプションで変更するコマンドを実行します。
# semanage fcontext -m -t git_rw_content_t '/var/lib/git(/.*)?'
各リポジトリディレクトリ下のhooks/ディレクトリは、この中にあるスクリプトを実行できるようSELinuxのタイプをgit_script_exec_tに割当てます。
# semanage fcontext -a -t git_script_exec_t '/var/lib/git/[^/]+/hooks(/.*)?'
/var/lib/gitディレクトリを作成し、念のためSELinuxのリソースを再割り当てしておきます。
# mkdir /var/lib/git
# restorecon -R /var/lib/git
リソースに割り当てられるタイプの調査
# semanage fcontext -l | grpe git
:
/var/lib/git(/.*)? all files system_u:object_r:git_sys_content_t:s0
/var/www/git(/.*)? all files system_u:object_r:git_content_t:s0
:
ドメインhttpd_tからリソースへのアクセス可能な操作の調査
ドメインhttpd_tからタイプgit_sys_content_tへの許可を調べるコマンドの実行例を以下に示します。
$ sesearch -A -s httpd_t -t git_sys_content_t
allow httpd_t file_type:dir { getattr open search };
allow httpd_t file_type:filesystem getattr;
allow httpd_t git_sys_content_t:dir { getattr ioctl lock open read search };
allow httpd_t git_sys_content_t:file { getattr ioctl lock map open read };
allow httpd_t git_sys_content_t:lnk_file { getattr read };
- ここで実行結果にあるリソースタイプがfilte_typeと末尾が_typeで終わる表記となっています。これはアトリビュートを示します。アトリビュートは複数のリソースタイプを束ねたものです。git_sys_content_tタイプもfile_typeに束ねられているのでsesearchコマンドの結果として表示されています。参考資料は次(p.16のスライドに記載あり)
www.slideshare.net
ドメインhttpd_tからタイプgit_content_tへの許可を調べるコマンドの実行例を以下に示します。
$ sesearch -A -s httpd_t -t git_content_t
allow httpd_t httpd_content_type:dir { getattr ioctl lock open read search }; [ httpd_builtin_scripting ]:True
allow httpd_t httpd_content_type:dir { getattr open search };
allow httpd_t httpd_content_type:dir { getattr open search }; [ httpd_builtin_scripting ]:True
allow httpd_t httpd_content_type:dir { getattr open search }; [ httpd_builtin_scripting ]:True
allow httpd_t httpd_content_type:dir { getattr open search }; [ httpd_builtin_scripting ]:True
allow httpd_t httpd_content_type:file { getattr ioctl lock map open read };
allow httpd_t httpd_content_type:file { getattr ioctl lock open read }; [ httpd_builtin_scripting ]:True
allow httpd_t httpd_content_type:lnk_file { getattr read }; [ httpd_builtin_scripting ]:True
アトリビュートに束ねられているリソースタイプの確認方法は次です。
$ seinfo --attribute=httpd_content_type -x
Type Attributes: 1
attribute httpd_content_type;
apcupsd_cgi_content_t
apcupsd_cgi_htaccess_t
:
git_content_t
git_htaccess_t
git_ra_content_t
git_rw_content_t
git_script_exec_t
httpd_sys_content_t
httpd_sys_htaccess_t
httpd_sys_ra_content_t
httpd_sys_rw_content_t
httpd_sys_script_exec_t
httpd_user_htaccess_t
httpd_user_ra_content_t
:
ドメインhttpd_tから、指定の操作が可能なリソースタイプを調査
$ sesearch -A -s httpd_t -c file -p write
:
allow httpd_t git_rw_content_t:file { append create getattr ioctl link lock open read rename setattr unlink write }; [ httpd_builtin_scripting ]:True
バックアップしていたgitリポジトリの復元
/var/lib/git ディレクトリの下に、バックアップしていたgitリポジトリを展開します。
(今回はtarで固めたバックアップファイルを単に展開)
# cd /var/lib/git
# tar xzf ~/git_swe_primus-20200423.git.tgz
# ls
swe.primus.git
# restorecon -R .
展開後、SELinuxのタイプが正しく付いていないこともあるので、restoreconで反映します。
また、次の手順でApache httpdをインストールした後で、ファイルのパーミッションをapacheに変更します。
Redmineと連携するリポジトリアクセス用Webサーバーには、Apache HTTPDを使います。
既にWebサーバーにはNginxを稼働させているので、ポート番号をずらしてApache HTTPDを稼働します。
Apache httpd は、モジュールとして提供されています。
# dnf module list httpd
CentOS-8 - AppStream
Name Stream Profiles Summary
httpd 2.4 [d] common [d], devel, minimal Apache HTTP Server
ヒント: [d]efault, [e]nabled, [x]disabled, [i]nstalled
現時点ではバージョンは1つだけ存在するので、そのままモジュールをインストールします。
# dnf module install httpd
:
Apache HTTPDが同時に複数のリクエストを受けて動作する際の並行処理の動作方式には複数の種類があります。Multi Processing Module: MPMと呼ばれる動作方式には、prefork、worker、eventの3種類があり、今回インストールしたCentOS 8のモジュールではデフォルトがeventになっています。
preforkは古のApache httpdからあるシングルスレッドプロセスを複数稼働させ、1リクエストを1プロセスで受ける方式、workerとeventはマルチスレッドプロセスを複数稼働させ、複数リクエストを1プロセスで受ける方式です。
- /etc/httpd/conf.modules.d/00-mpm.conf
この設定ファイルで方式に応じたsoをLoadModuleします。
さて、Redmine認証連携のためにmod_perlを使用しますが、このmod_perlは古いので event方式で支障なく動くものなのか確証がありません。そこで今回はMPM方式をpreforkに変更します。
- #LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
+ LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
- LoadModule mpm_event_module modules/mod_mpm_event.so
+ #LoadModule mpm_event_module modules/mod_mpm_event.so
また、子プロセス数等のpreforkの設定を記述するファイルを用意します。gitリポジトリへのアクセスに使うだけのHTTPDで、仮想マシンでCPU数もほとんどないので、起動プロセス数は最小限にします。
- /etc/httpd/conf.d/mpm.conf
<IfModule mpm_prefork_module>
StartServers 2
MinSpareServers 2
MaxSpareServers 2
ServerLimit 2
MaxRequestWorkers 2
MaxConnectionsPerChild 4
</IfModule>
既にNginxがポート80を使用しているので、Apache httpdはポートをデフォルトの80から8008等に変更します。SELinuxではhttpdプロセス(ドメインhttpd_t)がbindできるポートも制限がかけられています。
SELinuxの設定を変更することなく利用可能なポート番号の調べ方は次です。
http_port_t が対象とするポート番号を確認します。
# semanage port -l
:
http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000
:
ドメインhttpd_t は、http_port_t に対して name_bind 操作が許されています。
$ sesearch -A -s httpd_t
:
allow httpd_t http_port_t:tcp_socket name_bind;
:
-Listen 80
+Listen 8008
-ServerName
+ServerName www.torutk.com:8008
/etc/httpd/conf.d/ にある設定ファイルのうち使用しないものを削除(またはリネーム)します。
# cd /etc/httpd/conf.d
# mv welcome.conf welcome.conf.orig
# mv autoindex.conf autoindex.conf.orig
# mv ssl.conf ssl.conf.orig
# mv userdir.conf userdir.conf.orig
SSLモジュールを読み込まないよう設定を変更します。
- LoadModule ssl_module modules/mod_ssl.so
+ #LoadModule ssl_module modules/mod_ssl.so
まずはRedmine認証連携の前に、Apache httpdの起動とGitリポジトリ動作確認をします。
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/">
Require all granted
</LocationMatch>
認証はしないので、Require には"all granted"を記述します。
ポート8008を一時的に開きます。
# firewall-cmd --add-port=8008/tcp
httpdを自動起動設定かつ起動します。
# systemctl start --now httpd
リポジトリのパーミッションを変更します。
# cd /var/lib/git
# chown -R apache:apache *
リモートからhttp経由でクローンと変更のプッシュを行い、動作確認をします。
まずクローンを実行します。
D:\work> git clone http://www.torutk.com:8008/git/swe.primus.git
Cloning into 'swe.primus'...
remote: Enumerating objects: 739, done.
remote: Counting objects: 100% (739/739), done.
remote: Compressing objects: 100% (279/279), done.
remote: Total 739 (delta 232), reused 739 (delta 232)Receiving objects: 100% (739/739), 20.23 MiB | 20.23Receiving objects: 100% (739/739), 20.67 MiB | 20.06 MiB/s, done.
Resolving deltas: 100% (232/232), done.
変更をローカルでコミット後、プッシュします。
D:\work\swe.primus>git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 6 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 273 bytes | 136.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To http://www.torutk.com:8008/git/swe.primus.git
40ba3f1..6bd5f2d master -> master
Redmineの認証を利用して、Git(Apache HTTPD)の認証を行います。
Redmineに同梱されている Redmine.pm (Perlモジュール)を、httpdのmod_perlの検索パスに置きます。
# mkdir -p /etc/httpd/Apache/Authn
# sudo ln -s /var/lib/redmine/extra/svn/Redmine.pm /etc/httpd/Apache/Authn/Redmine.pm
このRedmine.pmをApache HTTPDから利用するために、HTTPDにmod_perl モジュールを追加する必要があります。mod_perlは少々古い仕組みで、CentOS 7以降はOS標準には含まれなくなっているので、EPELリポジトリから取得します。
EPELリポジトリ利用設定をインストールします。
# dnf install epel-release
EPELリポジトリからパッケージをインストールするのは限定的とするため、デフォルトではEPELリポジトリを無効とする設定に変更します。
- /etc/yum.repos.d/epel.repo
[epel]
name=Extra Packages for Enterprise Linux $releasever - $basearch
#baseurl=https://download.fedoraproject.org/pub/epel/$releasever/Everything/$basearch
metalink=https://mirrors.fedoraproject.org/metalink?repo=epel-$releasever&arch=$basearch&infra=$infra&content=$contentdir
- enabled=1
+ enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-8
mod_perlをインストールします。
# dnf --enablerepo=epel install mod_perl
:
perl-Digest-SHAをインストールします。
# dnf install perl-Digset-SHA
:
/etc/httpd/conf.d/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"
+ AuthUserFile /dev/null
+ RedmineDSN "DBI:mysql:database=redmine;host=localhost"
+ RedmineDbUser "redmine"
+ RedmineDbPass "XXXXXX"
+ RedmineGitSmartHttp yes
- Require all granted
+ Require valid-user
</LocationMatch>
以前のApache httpd + Gitでは、AuthUserFile /dev/null
は不要だったかと思いますが、現時点ではこの指定が必要です([authn_file:error] AH01619: AuthUserFile not specified in the configuration
が出る)。
この設定ファイルにはRedmineのMySQLデータベースへの接続情報が記載されるので、アクセス権を厳し目に設定します。
# chmod 0400 /etc/httpd/conf.d/git-redmine.conf
Apache httpdをリロードして動作確認します。
# systemctl reload httpd
Nginxのhttps経由でのアクセス
ここまでの設定でGitリポジトリへの読み書きのアクセスができるようになりました。
しかし、httpプロトコルでポート8008にアクセスしており、認証はBasicのため平文で流れてしまいます。
せっかくサーバーにSSLサーバー証明書を設置し、セキュアなアクセスができるようになっているので、SSLでGitリポジトリにアクセスしたいところです。
既にNginxがhttps(ポート443)を押さえているので、Apache HTTPDにSSL設定をすることができません。そこで、Ngixを経由してApacheにアクセスすることでSSLを利用します。
location ^~ /.well-known/acme-challenge/ {
root /usr/share/nginx/html;
}
+ location ^~ /git/ {
+ proxy_pass http://localhost:8008;
+ }
location / {
try_files /maintenance.html $uri/index.html $uri.html $uri @app;
}
この設定で、Nginxがポート80もしくは443でgitへのアクセスを受けると、localhostの8008に転送します。
しかし、これだけではSELinuxでエラーになってしまいます。ドメインhttpd_tが別なプロセスにTCP接続するには許可が必要になります。
SELinuxのブール値設定で、httpd_can_network_relay を on にします。
# getsebool -a | grep httpd_can_network
httpd_can_network_connect --> off
httpd_can_network_relay --> off
# setsebool -P httpd_can_network_relay on
# getsebool httpd_can_network_relay
httpd_can_network_relay --> on