UbuntuのRedmineをApacheで動かす (HTTPSとCORSに対応) (旧版)

はじめに

改訂された新しい記事があります!!

UbuntuのRedmineをApacheで動かす (HTTPSとCORSに対応)
UbuntuにインストールされたRedmineをApacheと組み合わせて稼働する手順を紹介します。自己証明書を使ったHTTPS化、またCORSの対応方法についても紹介しています。

以前の投稿でUbutntuにRedmine 5.1をインストールする手順について紹介しました。その続きとして、WebサーバーにApacheを使い、自己署名証明書 (いわゆるオレオレ証明書) を使ってHTTPS化したので、その手順を紹介します。CORS対応についても説明しています。

2023/04/11
Ubuntu 22.04.2 LTS Amd64で、Phusion Passengerのプロセスがスタートしない、というエラーを確認しました。この解決方法 (と思われるもの) について追記しています。Arm64版では発生せず確認が遅れました。このエラーで悩まれた方は申し訳ありません。

前記事

UbuntuにRedmine 5.1/Ruby 3.2をインストールする
UbuntuにRedmine 5.1をインストールする手順をステップバイステップで紹介します。Ruby 3.2, MySQL 8.0を使っています。CORS対応, HTTPS化についても解説しています。コピー&ペーストで実行可能です。
UbuntuにRedmine 5.1/Ruby 2.7をインストールする
UbuntuにRedmine 5.1をインストールする手順をステップバイステップで紹介します。Ruby 2.7, MySQL 8.0を使っています。CORS対応, HTTPS化についても解説しています。コピー&ペーストで実行可能です。

環境

ソフトウェアのバージョンは次の通りです。

  • OS: Ubuntu Server 22.04 LTS
  • Redmine: 5.1
  • Apache 2.4
  • Passenger 5.0

セットアップの手順

前記事でPumaを使って稼働確認を行ったところから継続します。

  • Apacheのインストール
  • ApacheのHTTPS化
  • Passengerのインストール
  • Passengerの構成
  • CORSの構成

特に指定しない場合は、作業はホームディレクトリ (/home/redmine) で行うものとします。

サービスの停止

以前の投稿で、Redmineの起動をサービス化している場合は、サービスを停止します。

sudo systemctl stop redmine
sudo systemctl disable redmine

Apacheのインストール

参考資料: Install and Configure Apache

まずレポジトリを最新化します。

sudo apt update

Apacheをインストールします。

sudo apt install apache2 apache2-dev

ブラウザで、http://<サーバーアドレス> にアクセスすると、デフォルトのページが表示されます。

ApacheのHTTPS化

参考資料:
1) How to enable HTTPS protocol with Apache 2 on Ubuntu 20.04
2) OPENSSL COOKBOOK by Ivan Ristic
(2023/04/08 参考資料を変更しopensslの使い方も更新しました)

本稿では自己署名証明書を使ってHTTPS化していますが、プライベート認証局を構築してHTTPS化する方法もあります。

プライベート認証局によるサーバー証明書の発行 - 悠閑探符
プライベート認証局(オレオレ認証局)を構築して、Webサーバー向けのサーバー証明書を発行する手順について紹介します。

自己署名証明書の作成

秘密鍵 (private.key) と署名リクエスト (request.csr) を作成します。秘密鍵は暗号化せずに作成しています。暗号化していると、Apacheを起動するサービス中でPass Phraseの入力を求められ、エラーとなるためです。

cd ~
openssl genpkey -out private.key -algorithm RSA -pkeyopt rsa_keygen_bits:2048
openssl req -new -key private.key -out request.csr

作成中、次のような質問が表示されるので、適宜指定してください。

Country Name (2 letter code) [AU]: JP
State or Province Name (full name) [Some-State]: Tokyo
Locality Name (e.g. city) []: Chiyoda
Organization Name (eg. company) [Internet Widgits Pty Ltd]: Hoge LLC
Organiztion Unit Name (eg. section) []: Home
Common Name (e.g. server FQDN or YOUR name) []: example.com

必須項目は、Country Name, Organization Name, Common Nameです。

Common Name (CN) は、Webブラウザに入力されたアドレスと比較されます。WebサーバーがFQDN (Fully Qualified Domain Name) を持っている場合は、それを指定します。IPアドレスしか持たない場合は、IPアドレスを指定します。

ブラウザによっては、CNではなく、SAN (Subject Alt Name) を参照するため、本来はSANの指定も必要です。ただそもそも自己署名証明書として例外を承認することで、SANは無視されるので、ここではSANの指定は省略しています。

証明書 (certificate.crt) を生成します。

openssl x509 -in request.csr -out certificate.crt -req -signkey private.key -days 365

正しく実行されると、Signature ok と表示されます。

プライベートキーのアクセス権限を制限しておきます。

chmod 400 private.key

プライベートキーを暗号化していないことで、セキュリティレベルが下がることが気になります。ただApacheのFAQにもこの方法が書かれています。

How can I get rid of the pass-phrase dialog at Apache startup time?

ここまでで次の3つのファイルが作成されます。

  • request.csr (署名要求)
  • certificate.crt (署名済み証明書)
  • private.key (秘密鍵)

このうちcertificat.crt, private.keyの2つのファイルを、次のようにフォルダを作成して保管します。

sudo mkdir /etc/certificate
sudo cp certificate.crt /etc/certificate
sudo cp private.key /etc/certificate

Apache SSLパラメータの定義

Apacheの構成ファイルに読み込むSSLパラメータを定義します。

sudo vim /etc/apache2/conf-available/ssl-params.conf

次の内容をコピーします。(筆者は不勉強のため解説不能 … 参考資料1) からのコピーです)

SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
    
    SSLProtocol All -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
    
    SSLHonorCipherOrder On
    
    
    Header always set X-Frame-Options DENY
    
    Header always set X-Content-Type-Options nosniff
    
    # Requires Apache >= 2.4
    
    SSLCompression off
    
    # SSLUseStapling on
    
    # SSLStaplingCache "shmcb:logs/stapling-cache(150000)"
    
    
    # Requires Apache >= 2.4.11
    
    SSLSessionTickets Off

SSLUseStampling, SSLStamplingCacheをコメントアウトしています。これらは、証明書の失効に関する設定です。外部の認証局によって認証された証明書の場合、その認証局に証明書が失効していないか問い合わせます。そのための設定になります。自己署名証明書の場合は、失効は自ら証明書を削除または置き換えますから、この設定は無用です。(あっても問題無いようですが、Apacheのログにエラーが出ます)

署名証明書をSSLに組み込む

HTTPSのリクエストを受けるサイト定義に、証明書を組み込みます。

定義ファイルを開きます。

sudo vim /etc/apache2/sites-available/default-ssl.conf

定義フィル内で次の記述を探します。

                    SSLCertificateFile      /etc/ssl/certs/ssl-cert-snakeoil.pem  
                    SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key

これを先に作成した証明書で置き換えます。

                    SSLCertificateFile      /etc/certificate/certificate.crt
                    SSLCertificateKeyFile /etc/certificate/private.key

参考資料1)では、ServerNameを追加するようになっています。筆者は後述するredmine.confの中で定義しました。

また参考資料1)では、ファイヤウォール (UFW) の設定が行われていますが、筆者は自宅内LANの環境なのでスキップしました。(sudo ufw statusで確認したところinactiveでした)

構成の適用

必要なモジュールを組み込むみます。

sudo a2enmod ssl
sudo a2enmod headers

SSLパラメータ定義を組み込みます。

sudo a2enconf ssl-params

HTTPSアクセスを受け付けるサイトの定義を組み込みます。

sudo a2ensite default-ssl

構成をテストします。

sudo apache2ctl configtest

次のメッセージが出ればOKです。ServerNameについては後で解消します。

AH00558: apache2: Could not reliably determine the server’s fully qualified domain name, using 127.0.1.1. Set the ‘ServerName’ directive globally to suppress this message
Syntax OK

Apacheサーバーを再起動します。

sudo systemctl restart apache2

ブラウザで、https://<server address> にアクセスします。

自己署名証明書のためブラウザから警告画面が表示されますが、指示に従ってアクセスを許可します。

デフォルトのページが表示されます。

セキュリティ警告の回避方法について

インターネット上に公開されるサーバーであれば、正式な証明書を組み込むべきでしょう (Let’s Encryptのような無料のサービスもありますし)。

イントラネットの場合は、イントラネット内にプラベート認証局を立てて、その認証局で署名した証明書を使うことで警告を回避できます (筆者もこの方法を使っています)。

Ubuntu 20.04で認証局(CA)をセットアップおよび設定する方法 | DigitalOcean
Ubuntu 20.04で実行するプライベート認証局は、クライアントとサーバー間の暗号化された接続を必要とするプログラムを設定、テスト、および実行できます。プライベートCAを使用して、インフラストラクチャ内のユーザー、サーバー、または個々の...

更に簡便に済ますなら、作成したオレオレ証明書 (certificate.crt) を、ルート証明書としてブラウザにインポートすることで、警告を消すことができます。ただしこの方法は、サーバーを追加すると、その度に証明書をブラウザに追加する必要があります。 またブラウザによっては、ルート証明書としてインポートできません。

自己署名証明書を作ってルート証明書として読み込んでみた
自己署名証明書 (オレオレ証明書) をルート証明書としてインポートしたときのブラウザの挙動をテストしてみました。ブラウザと証明書の作成方法によって挙動が違うことが判ったので、その結果を報告します。

別の方法として、もしドメインを所有しDNSの設定ができるのであれば、イントラネットであってもLet’s Encryptを利用できます。

Apacheの構成ファイルについて

Apacheの構成ファイルは構造化され複数のファイルに分かれています。

メインの構成ファイルとして、

/etc/apache2/apache2.conf

があり、ここに次のディレクトリ群に入っている個別の構成ファイルを読み込むようになっています。

/etc/apache2/(conf|mods|sites)-available
/etc/apache2/(conf|mods|sites)-enabled

個別の構成ファイルの入るディレクトリは、availableとenabledのペアとなっています。availableにファイルの実体を配置し、その中で実際に使うものをenabledにリンクしてあります。Apacheは起動時に、enabledからファイルを読み込んで、メインの構成ファイルに組み込みます。enabledにリンクするには、次のコマンドを使います。

a2ensite
a2enconf
a2enmod

Passengerのインストール

Phusion Passengerは、アプリケーションサーバーで、ここではRubyで書かれたアプリケーションの実行環境として使われます。Apacheの他、NGINXとの組み合わせもポピュラーなようです。

参考資料:
1) Installing Passenger on Ubuntu 20.04 LTS (with APT)
2) Introduction to configuring Passenger
3) Configuration reference for Passenger + Apache

インストールの準備をします。(Passengerレポジトリの取得準備のようですが詳細未確認)

sudo apt-get install -y dirmngr gnupg
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates

レポジトリを更新します。

sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger focal main > /etc/apt/sources.list.d/passenger.list'
sudo apt-get update

Passenger本体とApache用のモジュールをインストールします。

sudo apt-get install -y libapache2-mod-passenger

Apacheサーバーを再起動します。

sudo systemctl restart apache2

インストール結果を検査します。

sudo /usr/bin/passenger-config validate-install

検査対象を選択するように求められるので、Passenger, Apacheの両方を選択します。

What would you like to validate?
Use <space> to select.
If the menu doesn't display correctly, press '!'

   ⬢  Passenger itself
 ‣ ⬢  Apache

この結果、次のメッセージが表示されます。

 * Checking whether this Passenger install is in PATH... ✓
 * Checking whether there are no other Passenger installations... ✓
 * Checking whether Apache is installed... ✓

Everything looks good. :-)

Passengerのプロセスが起動していることを確認します。

sudo /usr/sbin/passenger-memory-stats

次のようにPassengerのプロセスが起動していることを確認します。

----- Passenger processes ------
PID     VMSize    Private  Name
--------------------------------
271481  294.7 MB  2.3 MB   Passenger watchdog
271484  849.9 MB  4.1 MB   Passenger core
### Processes: 2
### Total private dirty RSS: 6.39 MB
速報 (2023/04/11)

Ubuntu 22.04.2 LTS Amd64で構築したところ、上記でPassengerのプロセスが起動しない状況になりました。Apacheのエラーログ (/var/log/apache2/error.log) を見てみると、次のようなエラーメッセージが記録されていました。

PassengerAgent: error while loading shared libraries: libcrypto.so.1.1: cannot open shared object file: No such file or directory
[Tue Apr 11 11:51:20.032520 2023] [passenger:error] [pid 820:tid 140061081606016] *** Passenger could not be initialized because of this error: Unable to start the Phusion Passenger(R) watchdog: it seems to have crashed during startup for an unknown reason, with exit code 127

Stackoverflowの記事を参考に以下を試してみました。

wget http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.17_amd64.deb

sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2.17_amd64.deb

筆者の環境ではこれで解決しました。ただし他に副作用がないか十分な確認はできていません。(packageの中身をみると、openssl1.1向けのモジュールを追加するだけで、何かを上書きしたりはしないようです …)

ldconfig で確認すると下記のようにlibcrypto.so.1.1が追加でロードされていました。

	libcrypto.so.3 (libc6,x86-64) => /lib/x86_64-linux-gnu/libcrypto.so.3
	libcrypto.so.1.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libcrypto.so.1.1
	libcrypto.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libcrypto.so

Ubuntu 22.04.2 LTS Arm64 (Apple M2) で構築した場合は、このようなエラーは発生しませんでした。またldconfigで確認したところ、libcrypto.so.1.1はロードされていませんでした。

Phusion Passengerのビルドの違いで、libcrypto.so.1.1を要求するかしないかの違いがあるのかも知れません (筆者には判断不能 …)。

しかしもしAmd64とArm64で違いがあるとすると、今後の確認作業が2倍になるので、どうしたものかと …

Passengerのインストール方法には、RubyのGemとしてインストールする方法もあります。ネット上の記事ではこちらの方法を取っているものが多いようです。

Installing Passenger from RubyGem (with RVM)

Passengerの構成

PassengerでRedmineを呼び出すように構成ファイルを作成します。

sudo vim /etc/apache2/conf-available/redmine.conf

次の内容をコピーします。ServerNameは、自己署名証明書を作成した時に指定したもので置き換えます。

ServerName example.com
<IfModule mod_passenger.c>
    DocumentRoot /opt/redmine/public
    PassengerRoot /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini
    PassengerDefaultRuby /home/redmine/.rbenv/shims/ruby
</IfModule>

<Directory /opt/redmine/public>
    Allow from all
    Options -MultiViews
    Require all granted
</Directory>

PassengerRootの確認方法を失念しました。PassengerDefaultRubyについては、which rubyで確認しました。

「備忘録: UbuntuにRedmine 4.2/5.0をインストールする (CORS対応)」の古い記事では、ホーム (/home/redmine) にRedmineをインストールしていました。その記事から来られた方は、上記の定義で、/opt/redmine -> /home/redmine/redmine と読み替えてください。本記事では、Redmineのインストール先を/opt/redmineに変更しています。

HTTPSサイトの定義から、DocumentRootをコメントアウトします。(redmine.confと二重定義になるで)

sudo vim /etc/apache2/sites-available/default-ssl.conf
# DocumentRoot /var/www/html

設定を組み込みます。

sudo a2enconf redmine

Apacheサーバーを再起動します。

sudo systemctl restart apache2

ブラウザで、https://<サーバーアドレス> にアクセスすると、Redmineのページが表示されます。

CORSの構成

Cross-Origin Resource Sharing (CORS) は、ブラウザ内からRedmineのRest APIを呼び出す場合に必要になります。筆者の場合は、TrelloのPower-Up (プラグイン) を開発しており、CORS対応が必要となっています。通常のRedmineの使用では必要ありません。

CORSをサポートする方法には2つあります。

  • rack-corsの組み込み
  • ApacheによるHeaderとStatus Codeの書き換え

rack-corsの組み込み

前記事で使用した方法です。PassengerはRack対応ということで、rack-corsとの組み合わせでCORS対応を実現できます。

ApacheによるHeaderとStatus Codeの書き換え

より一般的 (?) な方法として、ApacheにHeaderとStatus Codeを書き換えさせることができます。

CORSのための設定ファイルを作成します。

sudo vim /etc/apache2/conf-available/cors.conf

次の内容をコピーします。

<Location />
  <IfModule mod_headers.c>
    Header always set Access-Control-Allow-Origin "*"
    Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT, PATCH"
    Header always set Access-Control-Max-Age "7200"
    Header always set Access-Control-Allow-Headers "Content-Type, x-redmine-api-key"
  </IfModule>

  <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteRule ^(.*)$ $1 [R=200,L]
  </IfModule>
</Location>

最初のヘッダー書き換えは、Access-Control-Allow-Originなど、CORSで要求されるヘッダー情報をレスポンスに追加します。

2番目はOPTIONSに対するステータスコードを書き換えます。OPTIONSはCORSに付随してブラウザからサーバーに発行されるリクエストで、CORSをサポートするかどうか事前に問い合わせます。RedmineはOPTIONSに対しては404を返すので、それを200に書き換えて返しています。

設定を組み込み、Apacheサーバーを再起動します。

sudo a2enconf cors
sudo a2enmod rewrite
sudo systemctl restart apache2

変更履歴

日時内容
2023/04/1122.04.2 LTS Amd64 でPassengerのプロセスが起動しない問題について記述
2023/04/08自己署名証明書の作成コマンドを変更 (OPENSSL COOKBOOKに準拠)
Apacheのインストール時に、apache2-devをインストールするように変更
2023/03/27自己署名証明書の作成時に指定する Common Name について説明を追加
2022/06/25自己署名証明書の作成時に、秘密鍵を最初から暗号化せずに生成するように変更
2022/06/21自己署名証明書の作成ステップを修正
> chmod 400 server.key -> chmod 400 private.key
> 上記chmodを別ラインに分割 (直前のコマンドでPass Phraseの入力が正しく行われないため)
redmine.confの記述を修正
> /home/redmine/redmine -> /opt/redmine
rack-corsの設定について補足
2022/06/20秘密鍵を復号化するように変更
セキュリティ警告の回避方法について記載
2022/06/10CORSに関する記述にrack-corsを追加
2022/06/07初版リリース