torutkのブログ

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

さくらVPSのOSをCentOS 8に更新、さらにRedmineを4.1に更新(続々々)

さくらVPSのOSをCentOS 8に、Redmineを4.1に更新、の続き(4日目)

先日の続きです。

torutk.hatenablog.jp

Redmineログのローテーション

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リポジトリの格納ディレクトリとSELinux許可

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設定では、書き込み操作およびスクリプトの実行操作が許可されていません。 そこで、書き込み操作と、必要なディレクトリに限定して実行操作を許可するリソースタイプを設定します。

  1. ドメインhttpd_tから/var/lib/git 以下のリソースに対して読み書き許可
  2. ドメインhttpd_tから/var/lib/git/<リポジトリ名>/hooks 以下のリソースに対して実行許可

ドメインhttpd_tから上述の許可を持つリソースタイプを調査したところ、次のタイプが適しています。

  1. git_rw_content_t
  2. 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 };

ドメイン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に変更します。

Apache httpd のセットアップ

Redmineと連携するリポジトリアクセス用Webサーバーには、Apache HTTPDを使います。 既にWebサーバーにはNginxを稼働させているので、ポート番号をずらしてApache HTTPDを稼働します。

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の動作方式

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>
Apache HTTPDのデフォルトポート変更

既に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;
  :
  • /etc/httpd/conf/httpd.conf を修正します。
-Listen 80
+Listen 8008

-ServerName
+ServerName www.torutk.com:8008
Apache httpdのデフォルト設定変更(不要な設定の削除)

/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
Apache httpd経由のGitリポジトリ動作確認

まずはRedmine認証連携の前に、Apache httpdの起動とGitリポジトリ動作確認をします。

  • /etc/httpd/conf.d/git-redmine.conf を新規作成します。
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
mod_perlのインストールとRedmine認証設定

Redmineの認証を利用して、Git(Apache HTTPD)の認証を行います。 Redmineに同梱されている Redmine.pm (Perlモジュール)を、httpdmod_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から利用するために、HTTPDmod_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が出る)。

この設定ファイルにはRedmineMySQLデータベースへの接続情報が記載されるので、アクセス権を厳し目に設定します。

# 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 HTTPDSSL設定をすることができません。そこで、Ngixを経由してApacheにアクセスすることでSSLを利用します。

  • /etc/nginx/conf.d/redmine.conf に追記
     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