nginxでalias下のfastcgiへのパスの渡し方

nginx + php-fpm の環境。
最近はドメインのトップからWordpressのようなシステムを置くことが増えたので
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
で良かったのですが、
URI上の一部分を別のディレクトリに置いていたりするパターンでは、rootの定義し直しではなくてaliasを使ったほうがよさげらしい。
しかしaliasにしてしまうと不都合もあるようで

server {
  server_name example.com;
  listen 80;
  root /var/www/example.com/;
  location /somepath/ {
    alias /var/www/somepath/;
    index index.php index.html;
    location ~ \.php$ {
      fastcgi_pass unix:/run/php-fpm/www.sock;
      fastcgi_index index.php;
      # fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      fastcgi_param SCRIPT_FILENAME $request_filename;
      fastcgi_param PATH_INFO $fastcgi_path_info;
      include fastcgi_params;
    }
  }
}

example.com/somepath/hoge.php をリクエストすると
$document_root$fastcgi_script_nameの場合 /var/www/somepath//hoge.php が渡されちゃう。スラッシュが2重になってしまうのでalias下では使えないことに。
$request_filenameの場合 /var/www/somepath/hoge.phpが渡る。こちらは望み通りの結果。

じゃあalias下じゃなくても$request_filename使えば良いのでは?と思って試した結果、
example.com/fuga.php のリクエストに対してどちらも /var/www/example.com/fuga.php となった。

大本がどこからのコピペだったか忘れたが、$request_filenameで良さそうだ。ただしrootとかaliasとかindexがしっかり定義されている文脈に限る。
rootのときは最後の/を調整してくれるけどaliasではしてくれないあたりからの違いってことか

参考
■落とし穴とよくある間違い | NGINX 日本語訳
■nginx $document_root$fastcgi_script_name と $request_filename の違い – Qiita

特定ドメインからのメールをチェックなしで通すテスト postfix

CentOS+postfixの話。
SPAMはなるべく受け取りたくないのでFromのチェックを厳しくしてます。
main.confで

smtpd_sender_restrictions =
        permit_mynetworks, # 信頼できるネットワークからは許可
        reject_non_fqdn_sender, # FQDNじゃない人は拒否
        reject_invalid_hostname, # ホスト名がおかしい人は拒否
        reject_unknown_sender_domain #ドメインのDNSレコード見ておかしかったら拒否

しかし世の中にはメールサーバーが正しくないけど受け取りたい相手というのもあるもので、特定ドメインからのメールだけは身元が怪しくても受け取っておきたいという場合もあります。
そんな場合は check_sender_access を入れると良いらしい。

smtpd_sender_restrictions =
        permit_mynetworks,
        check_sender_access regexp:/etc/postfix/sender_access.regexp,
        reject_non_fqdn_sender,
        reject_invalid_hostname,
        reject_unknown_sender_domain

sender_access.regexpでは、正規表現に合致したらOKと返すようにする

/^.+@example\.com$/i        OK

これでFromがexample.comドメインだったら、他の身元チェックをする前にOKになるのでメールが受信できるようになる。はず。
テストしようにも「身元が怪しい送信元」がすぐ手配できないので、一旦は様子見。
postfix checkしてreload。

Linuxファイルサーバーのレスキュー

ファイルサーバーが起動しなくなったというので引き取ってきました。ディスクトラブル、Sambaトラブル、NICトラブルと経験したのでメモ。

Dell PowerEdge T105にCentOS7、多用途目的のサーバーでしたが結局はファイルサーバーになってしまったので、今は別にNASも入っている現場ですので、今回は余っていたNASにデータをコピーしてお返しすればOK。

まずファイルサーバーの症状は、起動パーティションに不良セクタでしょうか。シングルユーザーモードで起動してfsck…と、今はXFSなのでxfs_repairでした。

  • CentOS起動時、カーネル選ぶところでeを押す
  • linuxなんちゃら~ ro ~ のところ以降を削除して rw init=/bin/sh としてCtrl+Xで起動
  • /sbin/xfs_repair /dev/sdd1 # 今回はsdd1でした
  • 再起動

次にデータレスキュー。CentOSのSambaをWindows10からのぞこうとしますが…エラー。見つからない系。これはSMBバージョンのアレですね、ということで /etc/samba/smb.confの[global]に min protocols = SMB2 を入れるも、一瞬見えたんだけど以降エラー。SMB3にして再起動してみたら途中のフォルダまでは見えたけど以降エラー。おかしい。
Windowsからの確認はそんなに重要じゃないので、CentOS→NASの直接コピーを試みることにする。

smbclientコマンドでNASにアクセスする。
smbclient //192.168.x.x/hoge で接続、ftpコマンドみたいな感じで進める。

cd old_samba # 目的ディレクトリに移動
prompt # ファイル毎に聞かない
recurse # 再帰的に辿ってコピー
mput * # ローカルの全ファイルを送信

しかし途中で NT_STATUS_IO_TIMEOUT だったり、 NT_STATUS_CONNECTIONS_DISCONNECTED だったり、エラーが出てしまう。
NASがのろくてタイムアウトしてしまうのだろうか?ファームウェアは最新だし、余計なサービスも切ってあるのでそんなに重くはなさそう。

諦めて外付けHDDに一旦コピーして持っていくかとも思ったが、手持ちのUSB-SATAアダプタが大容量HDDに対応してなくて、8TBの認識が1.27TBになっちゃう。amazonで新しいやつ(USB3.0対応、8TBまでいける)を注文して今日は諦め。

夕飯の支度でもするかーと思った瞬間降ってきた!NICが怪しい!
pingしてみたらタイミングにより5~15%ほどパケロスしている。これだな。

ということで家族にはすみませんが先に風呂にでも入ってもらって早速見てみる。
yum -y instal pciutils
lspci
で確認するとBroadcom neXtreme BCM5722だそうで。
Linuxのドライバを確認するとRHEL7用のドライバrpmが存在。
■Broadcom Linux RPM packaged driver updates for NetXtreme Ethernet adapters for the 19.0 update. | ドライバの詳細 | Dell 日本
CentOS7.7ですがRHEL7.2用rpmをインストール。再起動してpingは問題なさそう。

以後、再度smbclientでmput *。無事にコピーできましたとさ。

WordPressへの攻撃対処 ThemeGrill Demo Importer脆弱性

「Wordpressが消えちゃったんですけど!」と猛烈な勢いで連絡をいただいたので、他のことをストップして対処しました。
いろいろと見てみると、データベースが初期化されてました。

MySQLが吹っ飛んだのかな?とも思いましたが、MySQL4.x時代ならともかく、もう10年以上そんな事態は見たことがありません。

こちらのサイトは同一データベースに複数のwordpressを、DBのprefixで分けて設置していましたが、今回初期化されていたのは該当の1サイトだけ。他のサイトは無事でした。そして吹っ飛んだ側のサイトもwp_postが1件だけ存在。インストール時のデフォルトの投稿記事です。どうやら吹っ飛んだ感じではなさそうです。

格闘すること2時間弱、アクセスログに見つけました。

45.129.xxx.xxx - - [18/Feb/2020:HH:MM:SS +0900] "GET /wp-admin/admin-post.php?do_reset_wordpress=1 HTTP/1.1" 302 5 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36" "-"
45.129.xxx.xxx - - [18/Feb/2020:HH:MM:SS +0900] "GET /wp-admin/themes.php?page=demo-importer&browse=all&reset=true; HTTP/1.1" 302 5 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36" "-" 

do_reset_wordpress=1ってなんじゃいな!w
探してみると ThemeGrill Demo Importerプラグインの脆弱性だそうで、1日前に報告されています。ゼロデイ攻撃っぽい。
このプラグインはThemeGrillというテーマシリーズがあって、そちらの必須プラグインみたいです。いろいろテーマを試すには便利な仕組みのようです。

とりあえずこのプラグインは停止、削除。
データベースをバックアップから復旧して、サイトの記事は少し前の状態に戻ってしまいましたが、回復。

ついでにwp-admin以下にアクセス制限をかけます。誰でもGET/POSTできるとか危ない。

余計なテーマやプラグインは削除しておけとは思いますが、使用中のテーマの必須プラグインなんだから、普通は止めちゃいけないと思いますわな…

■Critical Bug in WordPress Theme Plugin Opens 200,000 Sites to Hackers

PSQL Windows ODBC Linux SQL 接続

要約:
DB MagicのBtrieveファイルをPSQLを介して、LinuxのPHPからODBC経由で読み書きできる
なかなか苦労したので、こんなことやる人少ないだろうけど、記しておく

前置き

DOS時代から聞いたことがあったBtrieveというファイル内DBの仕組み。固定長データベースですからなかなかとっつきにくいし、Windows時代になって長いこと過ぎて、既に使われていないのでは…と思ったら結構あるもんですね。DB Magic(UniPaaSを経て今はMagic XPA)で採用されてるからかな。

で、そのBtrieveファイルをもとにSQLとして扱える仕組みが提供されており、だったらPHPからBtrieve読み書きできたりしちゃうんじゃないのーということで、そういう話が始まりまして、結構苦労したので記します。日本語のソースもあんまりなかったし。

Btrieveを使ったDBMSとしてActian PSQLというのがあります(以前はPervasive PSQLでした)。このPSQLエンジンがあれば、バックエンドはBtrieveですが、SQLを使って問い合わせができます。
そしてさらにPSQLはODBCインターフェースを提供しているので、Windowsはもちろん、LinuxでもODBCドライバが存在しています。

ということでWindows側でBtrieve、PSQL、ODBC、Linux側でPSQLドライバ、unixODBC、php-odbcと重ねていくことで、PHPからWindows上のPSQLエンジンを利用することができるわけです。

Windows側の準備

Actian PSQLをセットアップすると大体入ってます。Magic XPA付属のPSQLでも入ってました。適当にBtrieveファイルをもとにPSQLが利用できるようです。

Btrieveデータファイルだけがある状態ではSQLは利用できません。
PSQL Control Centerから「データベースの作成」を行い、データパスにBtrieveデータがあるフォルダを指定してやります。
さらにPSQL DDF Builderを起動し、作ったデータベースのデータパス内から使用するデータファイルを探し出します。そこで右クリック「テーブル定義の作成」です。

Btrieveファイルは固定長データベースですが、デリミタがありませんので、あらかじめ「Xバイト目からはYというフィールドで型がINTEGERだから4バイトです」という定義が必要なのです。データファイルがあるだけではPSQLはこれを知りませんので、定義を別途作ってPSQLに読ませます。この作業をするのがDDF Builderです。きっとData Definition Fileかなんかの略です。

DDFができたらPSQL上でテーブルが扱えるようになっています。初回開くとインデックスが作成されたりしてRDBMSっぽいです。スキーマをエクスポートしておくと、他の環境にデータファイルを持っていった際にDDFをインポートするだけで動くようになります。
ちなみにスキーマの頭には CREATE TABLE “hoge” USING “hoge.dat” なんて書かれていて、ああファイルに対するテーブル定義なんだなぁと確認できます。

WindowsのODBCデータソースからPervasive ODBC Unicode Interfaceなんかのドライバを使ってDSNを作れます。フリーソフトのcse(Common SQL Environment)などで接続すると、DB内にDDFで定義されたテーブルが存在しているように見えるのがわかります。

PSQLはTCPポート1583で動くらしいので、他のマシンからアクセスさせるためにファイヤウォールの解除をしておきます。いまどきはポート指定じゃなくてPSQLエンジンのプログラムを指定したほうがいいんだろうけど、やってないからわかりません。

Linux側の準備

今回はCentOS8上でApache+php-fpmを用意してましたのでこれでやります。
CentOS8上にあるApache2.4はmod_phpじゃなくてphp-fpmを使うようになってますのでそのへんセットアップしておきます。
Apache2.4はデフォルトで全体がdenyされているのではじめてセットアップするとforbiddenの嵐で大抵ハマります。

とりあえずパッケージとして php-odbc unixODBC あと使う人は php-pdo 他使うモノを入れておきます。

Linux用のPSQL Clientをダウンロードしてきます。
■PSQL v12 SP1 インストール用ファイル(ダウンロード情報)
64bit Linux rpmをインストールします。あっさり終わりますが、環境設定は自前でやる必要があります。

psqlユーザーが作られているので、~psql/.bashrc に書いてある環境変数をコピーしておけばrootでも動きます。なんかごちゃごちゃ書いてありますが、元のPATHが消えたりしてて困るので必要そうな部分だけコピーして書き直します。

export PVSW_ROOT=/usr/local/psql
export PATH=$PVSW_ROOT/bin:$PATH
export LD_LIBRARY_PATH=$PVSW_ROOT/lib64:$PVSW_ROOT/bin:$LD_LIBRARY_PATH
export MANPATH=$PVSW_ROOT/man:$MANPATH
export BREQ=$PVSW_ROOT/lib
export LD_BIND_NOW=1

PSQL ClientにDSN追加を命じます(内部的に何をしているのかわからんけど、少なくともodbc.iniファイルを置くだけではダメなのでコマンド実行は必須のようです)
# dsnadd -dsn=MYDSNNAME -db=DBNAME -host=WindowsHostIP
/usr/local/psql/etc/odbc.ini が作成されるので、これを /etc/odbc.ini に追記しておきます。
また /usr/local/psql/etc/odbcinst.ini を同様に /etc/odbcinst.ini に追記しておきます。

お気づきかもしれませんが、PSQL関連の環境変数がないとPSQL ClientおよびODBCドライバは動作しません。なのでphp-fpmに環境変数を渡す必要があります。
CentOS8ではphp-fpmはsystemd経由で動いてますので、 /etc/systemd/system/php-fpm.service.d/ の中に pvsw.conf とでもファイルを作成して、環境変数を渡してやります。

[Service]
Environment=LD_LIBRARY_PATH=/usr/local/psql/lib64:/usr/local/psql/bin:/usr/lib Environment=PATH=/usr/local/psql/bin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin:/sbin:/usr/sbin:/root/bin
Environment=PVSW_ROOT=/usr/local/psql
Environment=BREQ=/usr/local/psql/lib

設定ファイルの変更ということになるので
# systemctl daemon-reload
# systemctl php-fpm reload
しておきます。

ここまでで準備完了です。適当にコードを書いて走らせてテストしましょう。

$conn = odbc_connect('MYDSNNAME', '', ''); // user/passは空で良い
$odbc_q = odbc_exec($conn, "SELECT * FROM sometable;");
$odbc_r = odbc_fetch_array($odbc_q);

なお、Windows側は文字コードがsjisですので、SQL文は適宜mb_convert_encodingなどしてやる必要があります。文字コードが適切なら日本語カラム名もクォート等なしで通るようです。