基本的にNginxではPHPを利用するが、たまにPerl CGIを動かしたいときがある。
そんな時には「FCGI Wrap」と「Spawn FCGI」を利用してNginxでPerl CGIを利用可能にするが、更にApacheのsuEXECのように各ユーザー権限で安全に実行する方法を解説する。
※動作確認環境
CentOS 7.6
Nginx 1.14.2
FCGI WrapとSpawn FCGIのインストール
「epel-release」の確認
まずはじめに、NginxでPerl CGIを実行するための準備を行う。
FCGI WrapとSpawn FCGIは「epel」リポジトリからインストールするので、念のためにリポジトリを確認する。
# yum -y install epel-release
インストールされていなければ「epel-release」のインストールが始まるので、インストールが終了したらリポジトリの設定も行っておきましょう。
# vi /etc/yum.repos.d/epel.repo
【備考】
enabledの値:[0]許可時のみ、[1]リポジトリ常時利用
※当方は基本的にepelリポジトリのenabledは全て[0]で運用しています。
FCGI WrapとSpawn FCGIのインストール
FCGI WrapとSpawn FCGIをyumで一気にインストールします。
# yum -y --enablerepo=epel install fcgiwrap spawn-fcgi
読み込んだプラグイン:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
epel/x86_64/metalink | 7.6 kB 00:00:00
* base: ftp.iij.ad.jp
* epel: ftp.iij.ad.jp
* extras: ftp.iij.ad.jp
* remi-safe: ftp.riken.jp
* updates: ftp.iij.ad.jp
epel | 3.2 kB 00:00:00
(1/2): epel/x86_64/updateinfo | 948 kB 00:00:00
(2/2): epel/x86_64/primary | 3.6 MB 00:00:00
epel 12852/12852
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> パッケージ fcgiwrap.x86_64 0:1.1.0-9.20181108git99c942c.el7 を インストール
--> 依存性の処理をしています: libfcgi.so.0()(64bit) のパッケージ: fcgiwrap-1.1.0-9.20181108git99c942c.el7.x86_64
---> パッケージ spawn-fcgi.x86_64 0:1.6.3-5.el7 を インストール
--> トランザクションの確認を実行しています。
---> パッケージ fcgi.x86_64 0:2.4.0-25.el7 を インストール
--> 依存性解決を終了しました。
依存性を解決しました
=================================================================================================================
Package アーキテクチャー バージョン リポジトリー 容量
=================================================================================================================
インストール中:
fcgiwrap x86_64 1.1.0-9.20181108git99c942c.el7 epel 21 k
spawn-fcgi x86_64 1.6.3-5.el7 epel 18 k
依存性関連でのインストールをします:
fcgi x86_64 2.4.0-25.el7 epel 47 k
トランザクションの要約
=================================================================================================================
インストール 2 パッケージ (+1 個の依存関係のパッケージ)
総ダウンロード容量: 86 k
インストール容量: 155 k
Downloading packages:
(1/3): fcgi-2.4.0-25.el7.x86_64.rpm | 47 kB 00:00:00
(2/3): fcgiwrap-1.1.0-9.20181108git99c942c.el7.x86_64.rpm | 21 kB 00:00:00
(3/3): spawn-fcgi-1.6.3-5.el7.x86_64.rpm | 18 kB 00:00:00
-----------------------------------------------------------------------------------------------------------------
合計 288 kB/s | 86 kB 00:00:00
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
インストール中 : fcgi-2.4.0-25.el7.x86_64 1/3
インストール中 : fcgiwrap-1.1.0-9.20181108git99c942c.el7.x86_64 2/3
インストール中 : spawn-fcgi-1.6.3-5.el7.x86_64 3/3
検証中 : fcgiwrap-1.1.0-9.20181108git99c942c.el7.x86_64 1/3
検証中 : fcgi-2.4.0-25.el7.x86_64 2/3
検証中 : spawn-fcgi-1.6.3-5.el7.x86_64 3/3
インストール:
fcgiwrap.x86_64 0:1.1.0-9.20181108git99c942c.el7 spawn-fcgi.x86_64 0:1.6.3-5.el7
依存性関連をインストールしました:
fcgi.x86_64 0:2.4.0-25.el7
完了しました!
Spawn FCGIの設定
Spawn FCGIの設定ファイル /etc/sysconfig/spawn-fcgi を以下のように書き換えます。(初期状態の設定は全て#でコメント化されているので末尾に追加してもOKです。)
# vi /etc/sysconfig/spawn-fcgi
FCGI_SOCKET=/var/run/fcgiwrap.socket
FCGI_PROGRAM=/usr/sbin/fcgiwrap
FCGI_USER=nginx
FCGI_GROUP=nginx
FCGI_EXTRA_OPTIONS="-M 0770"
OPTIONS="-u $FCGI_USER -g $FCGI_GROUP -s $FCGI_SOCKET -S $FCGI_EXTRA_OPTIONS -F 1 -P /var/run/spawn-fcgi.pid -- $FCGI_PROGRAM"
【備考】
基本的な設定なので、実行ユーザと実行グループは「nginx」、起動プロセス数はOPTIONSの「-F 1」でプロセス数「1」に設定してあります。
Spawn FCGIの起動設定
systemdのリロード
# systemctl daemon-reload
Spawn FCGIの自動起動をオン。
# systemctl enable spawn-fcgi
Spawn FCGIを起動。
# systemctl start spawn-fcgi
Nginxの設定
Nginxで「.cgi」ファイルが実行できるように設定します。
server {
...省略...
location ~ \.cgi$ { # Perl CGI設定
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
fastcgi_index index.cgi;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
...省略...
}
SSI(Server Side Includes)も利用したい場合は「location / { ~ }」内に以下の設定も追加します。
server {
...省略...
location / {
...省略...
ssi on; # SSI設定
ssi_silent_errors off; # SSIエラー設定
}
...省略...
}
Nginxを再起動します。
# systemctl restart nginx
ここまでで通常のPerl CGIが実行可能になります。
テスト用のPerl CGIをドキュメントルート以下にアップロードまたは作成し、パーミッション(アクセス権)を750にしてブラウザで確認してみましょう。
テスト用cgiの例
#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "Perl CGI OK!";
exit;

ブラウザでこんな感じに表示されればOKです。
動作状況確認
既に上記の基本設定でPerl CGIがNginxで実行可能になっていますが、現在の動作状況は以下のとおりです。
- 実行ユーザ: nginx
- 実行グループ: nginx
- 実行可能なファイルのパーミッション: 750
※Spawn FCGIの設定ファイル「/etc/sysconfig/spawn-fcgi」にて「FCGI_USER」と「FCGI_GROUP」の設定を変更すれば実行ユーザ、実行グループの変更も可能です。
基本設定のみでの問題点
一つのサーバーに一つのドメインで運用するならばこのままで問題ありませんが、バーチャルドメインで複数のドメインを運用する場合にはセキュリティ的に問題が発生します。
なので、セキュリティ対策のために、ApacheのsuEXECのようにドメインごとの各ユーザの権限でPerl CGIが実行出来るようにカスタマイズします。
Perl CGIのsuEXEC化開始
suEXEC化のために行う設定の内容
ここからが本題です。
NginxにおけるPerl CGIのsuEXEC化処理の設定内容は以下のとおりです。
- ドメインごとにFCGI Wrapのサービスを新規作成する。
- ドメインごとのFCGI Wrapサービスは実行グループ・実行ユーザをドメインのユーザ・グループに変えてSpawn FCGIを実行させる。
- Nginxでドメインごとに「fastcgi_pass」のunixソケットを変える。
これによりドメインごとに実行ユーザ、実行グループを変えてPerl CGIを実行させることができます。
それでは設定を開始します。
サービス新規作成
サービスを新規作成してsystemdに登録します。
※ここでは当サイトの「onoredekaiketsu.com」でテストしています。(実際に運用する場合はご利用中のドメイン名に変えて設定してください。コピペで使えるひな形も下のほうに記載してあります。)
# vi /usr/lib/systemd/system/fcgiwrap-onoredekaiketsu.com.service
[Unit]
Description=Perl CGI Server onoredekaiketsu.com
After=nss-user-lookup.target network.target
[Service]
Type=forking
PIDFile=/var/run/spawn-fcgi-onoredekaiketsu.com.pid
ExecStart=/usr/bin/spawn-fcgi -u onoredekaiketsu.com -g onoredekaiketsu.com -s /var/run/fcgiwrap-onoredekaiketsu.com.socket -S -M 0770 -F 4 -P /var/run/spawn-fcgi-onoredekaiketsu.com.pid -- /usr/sbin/fcgiwrap
PrivateTmp=true
Restart=on-failure
[Install]
WantedBy=multi-user.target
【備考】
ExecStartの「-u」オプションで実行ユーザ、「-g」オプションで実行グループを指定しています。「-F」オプションで起動プロセス数を4に設定していますが、この値は任意で調整してください。
サービスの起動設定
systemdのリロード
# systemctl daemon-reload
サービスの自動起動をオン。
# systemctl enable fcgiwrap-onoredekaiketsu.com
サービス起動。
# systemctl start fcgiwrap-onoredekaiketsu.com
Nginxの設定
Nginxで「fastcgi_pass」のunixソケットを変更します。
旧:fastcgi_pass unix:/var/run/fcgiwrap.socket;
新:fastcgi_pass unix:/var/run/fcgiwrap-onoredekaiketsu.com.socket;
server {
...省略...
location / {
...省略...
ssi on; # SSI設定
ssi_silent_errors off; # SSIエラー設定
}
location ~ \.cgi$ { # Perl CGI設定
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/var/run/fcgiwrap-onoredekaiketsu.com.socket;
fastcgi_index index.cgi;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
...省略...
}
Nginxを再起動します。
# systemctl restart nginx
これで設定が完了です。
コピペ用ひな形
※コピペで使えるひな形を置いておきます。
[example.com]を利用中のドメインに変えてご利用ください。
== ここからひな形 ==
サービスを新規作成してsystemdに登録
# vi /usr/lib/systemd/system/fcgiwrap-[example.com].service
[Unit]
Description=Perl CGI Server [example.com]
After=nss-user-lookup.target network.target
[Service]
Type=forking
PIDFile=/var/run/spawn-fcgi-[example.com].pid
ExecStart=/usr/bin/spawn-fcgi -u [example.com] -g [example.com] -s /var/run/fcgiwrap-[example.com].socket -S -M 0770 -F 4 -P /var/run/spawn-fcgi-[example.com].pid -- /usr/sbin/fcgiwrap
PrivateTmp=true
Restart=on-failure
[Install]
WantedBy=multi-user.target
systemdのリロード
# systemctl daemon-reload
サービスの自動起動をオン。
# systemctl enable fcgiwrap-[example.com]
サービス起動。
# systemctl start fcgiwrap-[example.com]
Nginxで「fastcgi_pass」のunixソケットを変更。
server {
...省略...
location / {
...省略...
ssi on; # SSI設定
ssi_silent_errors off; # SSIエラー設定
}
location ~ \.cgi$ { # Perl CGI設定
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/var/run/fcgiwrap-[example.com].socket;
fastcgi_index index.cgi;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
...省略...
}
Nginxを再起動。
# systemctl restart nginx
== ひな形おわり ==
動作状況確認
ちゃんとNginxにおけるPerl CGIのsuEXEC化が行われているか実際にテストしてみます。
以下のPerl CGIをサーバにアップしてテストします。(※パーミッション700)
#!/usr/bin/perl
use strict;
# 各種情報の取得
my $http_host = $ENV{'HTTP_HOST'}; # ターゲットホスト名取得
my $web_server = $ENV{'SERVER_SOFTWARE'}; # WEBサーバー名取得
my $script_name = $ENV{'SCRIPT_NAME'}; # Perl CGIスクリプト名取得
my $script_permission = substr((sprintf "%03o", [ stat $ENV{'SCRIPT_FILENAME'} ]->[2]), -3); #Perl CGIスクリプトのパーミッションを取得
my $executing_group = getpwuid($<); # Perl CGIの実行グループ名取得
my $executing_user = getpwuid($<); # Perl CGIの実行ユーザ名取得
# HTML出力
print "Content-type: text/html\n\n";
print <<EOF;
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>NginxでPerl CGIをsuEXEC化して動かすテスト</title>
</head>
<body>
<h1>NginxでPerl CGIをsuEXEC化して動かすテスト</h1>
<ul>
<li>ターゲットホスト名(HTTP_HOST): $http_host</li>
<li>WEBサーバー(WEB server): $web_server</li>
<li>CGIスクリプト名(Perl CGI script name): $script_name</li>
<li>パーミッション(Permission): $script_permission</li>
<li>実行グループ(Executing_group): $executing_group</li>
<li>実行ユーザ(Executing_user): $executing_user</li>
</ul>
<p>
<a href="https://onoredekaiketsu.com/suexec-perl-cgi-with-nginx/"><<記事「NginxでPerl CGIをsuEXEC化して各ユーザー権限で実行する方法」に戻る</a>
</p>
</body>
</html>
EOF
exit;
ブラウザで確認します。(以下のURLで実際に動作を確認できます。)
動作確認URL: https://onoredekaiketsu.com/perl-cgi/test.cgi

動作確認OKです。
- 実行グループ: onoredekaiketsu.com
- 実行ユーザ: onoredekaiketsu.com
- 実行しているPerl CGIファイルのパーミッション: 700
Perl CGIスクリプトのパーミッションも700で実行可能なのでセキュリティは高いですね。
※Perl CGIスクリプトを置いておくディレクトリもパーミッション700でOKです。
まとめ
NginxでPerl CGIを動かしたい場合には、「FCGI Wrap」と「Spawn FCGI」をインストールし、ドメインごとのサービスをsystemdで新規作成してsuEXEC化。更にファイルのパーミッション「700」でPerl CGIを実行してセキュリティを高める。
これで安心してPerl CGIを利用することができます。
最近はPerl CGIをWEBサイトで動かす人が減ってきましたが、まだまだPerlは全然使えます。配布されているフリーのPerl CGIやMovable TypeなどもNginxで普通に動きます。
ちょっとした処理ならPerlでサクッと書いてしまうのが一番楽ですね。
以上で解決です。

