基本的に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」リポジトリからインストールするので、念のためにリポジトリを確認する。
1 |
# yum -y install epel-release |
インストールされていなければ「epel-release」のインストールが始まるので、インストールが終了したらリポジトリの設定も行っておきましょう。
1 |
# vi /etc/yum.repos.d/epel.repo |
【備考】
enabledの値:[0]許可時のみ、[1]リポジトリ常時利用
※当方は基本的にepelリポジトリのenabledは全て[0]で運用しています。
FCGI WrapとSpawn FCGIのインストール
FCGI WrapとSpawn FCGIをyumで一気にインストールします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# 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です。)
1 |
# vi /etc/sysconfig/spawn-fcgi |
1 2 3 4 5 6 |
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のリロード
1 |
# systemctl daemon-reload |
Spawn FCGIの自動起動をオン。
1 |
# systemctl enable spawn-fcgi |
Spawn FCGIを起動。
1 |
# systemctl start spawn-fcgi |
Nginxの設定
Nginxで「.cgi」ファイルが実行できるように設定します。
1 2 3 4 5 6 7 8 9 10 |
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 / { ~ }」内に以下の設定も追加します。
1 2 3 4 5 6 7 8 9 |
server { ...省略... location / { ...省略... ssi on; # SSI設定 ssi_silent_errors off; # SSIエラー設定 } ...省略... } |
Nginxを再起動します。
1 |
# systemctl restart nginx |
ここまでで通常のPerl CGIが実行可能になります。
テスト用のPerl CGIをドキュメントルート以下にアップロードまたは作成し、パーミッション(アクセス権)を750にしてブラウザで確認してみましょう。
テスト用cgiの例
1 2 3 4 5 |
#!/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」でテストしています。(実際に運用する場合はご利用中のドメイン名に変えて設定してください。コピペで使えるひな形も下のほうに記載してあります。)
1 |
# vi /usr/lib/systemd/system/fcgiwrap-onoredekaiketsu.com.service |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[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のリロード
1 |
# systemctl daemon-reload |
サービスの自動起動をオン。
1 |
# systemctl enable fcgiwrap-onoredekaiketsu.com |
サービス起動。
1 |
# 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;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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を再起動します。
1 |
# systemctl restart nginx |
これで設定が完了です。
コピペ用ひな形
※コピペで使えるひな形を置いておきます。
[example.com]を利用中のドメインに変えてご利用ください。
== ここからひな形 ==
サービスを新規作成してsystemdに登録
1 |
# vi /usr/lib/systemd/system/fcgiwrap-[example.com].service |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[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のリロード
1 |
# systemctl daemon-reload |
サービスの自動起動をオン。
1 |
# systemctl enable fcgiwrap-[example.com] |
サービス起動。
1 |
# systemctl start fcgiwrap-[example.com] |
Nginxで「fastcgi_pass」のunixソケットを変更。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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を再起動。
1 |
# systemctl restart nginx |
== ひな形おわり ==
動作状況確認
ちゃんとNginxにおけるPerl CGIのsuEXEC化が行われているか実際にテストしてみます。
以下のPerl CGIをサーバにアップしてテストします。(※パーミッション700)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#!/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でサクッと書いてしまうのが一番楽ですね。
以上で解決です。