nginxでリバースプロキシしているときにupstreamからのレスポンスヘッダを書き換える。

やりたいこと

いろいろあって、nginxでリバースプロキシしているときに、upstreamから渡ってくるlocationヘッダの中身を書き換えないといけない場面に出くわしました。
普通ならproxy_redirectでどうにかするものですが、upstreamが返してくるlocationヘッダが自分の管理しているサイトとは全然別のところで、それをどうにか書き換えないといけません。ちなみにいじっている環境はモジュールが導入できないので、デフォルトで有効になっている機能でどうにかする必要があります。

やりたいことを図にするとこんな感じですね。

これを

upstream--(Location: http://example.com?hogehoge)--> nginx--> クライアント

こうしたい。

upstream--(Location: http://example.com?hogehoge)--> nginx (Location: http://example.jp?hogehoge)--> クライアント

失敗した方法

最初「if使って書き換えればいいじゃん」と思って試したのが以下。

location / {
  if ($upstream_http_location ~ example.com(.*)){
     proxy_hide_header location;
     add_header location http://example.jp$1;
  }
}

おそらくifの評価タイミングが$upstream_http_locationに値が設定される後ではなく前のため、どう頑張っても評価がfalseになってしまいました。そもそもlocation内でifを使うのは事故の元なのであんまりよくないです。
If Is Evil | NGINX

期待通りに動作した方法

じゃあmapならどうだと思って試したのが以下。

map $upstream_http_location $location{
  ~example.com(.*) http://example.jp$1;
  default $upstream_http_location;
}
location / {
  proxy_hide_header location;
  add_header location $location
}

動きました。ifは評価タイミングがクライアントからリクエストを受けた直後で、mapはレスポンス返す直前なのかな…。そこは理解できていません。
Module ngx_http_rewrite_module
Module ngx_http_map_module

このサイト(CentOS 7)のPHPを5.4から7.1にアップグレードした。

グーペのPHPバージョンを5.2から7.1にアップグレードしました – ペパボテックブログ
すぐ詐欺にあいそう、とよくいわれるくらい何かに影響を受けやすいのと、このサーバ上で動いているサービスのうち、PHPで動作しているものがWordPressしかないため、アップグレードすることにしました。
ちなみにWordPressの推奨PHPバージョンは7以上です。

アップグレード前

 !  ~  rpm -qa | grep php                                                                                                  2017年06月26日 22時05分45秒
php-mbstring-5.4.16-42.el7.x86_64
php-fpm-5.4.16-42.el7.x86_64
php-mysql-5.4.16-42.el7.x86_64
php-gd-5.4.16-42.el7.x86_64
php-common-5.4.16-42.el7.x86_64
php-pdo-5.4.16-42.el7.x86_64

yumのデフォルトバージョン、かつ最小構成ですね。ほぼ何も入ってなくて笑いました。ちなみにこのブログはphp-fpmとnginxの構成で動いています。
トラブル起こると嫌なのでfishからbashに切り替えてから作業しました。

インストール

$ sudo yum remove php-* # パッケージ削除
$ sudo yum install http://rpms.famillecollet.com/enterprise/remi-release-7.rpm # remiリポジトリ追加
$ sudo yum install --enablerepo=remi-php71 php-fpm php-mbstring php-mysqlnd php-pdo php-gd php-common

php-mysqlではなくphp-mysqlndを使えという記事が多かったのでこちらを採用しました。あと、場合によってはyum install epel-releaseしないとだめかもしれませんね。

php-fpmの設定

設定ファイルは/etc/php-fpm.d/www.confにありました。
userとgroupをnginxの実行ユーザに合わせています。

user = www-data
group = www-data

; unix socketでリスンしてるので設定を変更。ファイルのパーミッションをnginxが読めるように設定
listen = /var/run/php5-fpm.sock
listen.owner = www-data
listen.group = www-data

無事動くようになりました。x-powered-byはPHP/7.1.6ですね。ちなみに私はx-powered-byでバージョン隠さない派です。

サーバのストレージ容量が半分切ったので原因を調べた。

Logwatch、普段はストレージサイズの項目読んでないのですが、久しぶりに見てみたらこんな感じで半分切っててびっくりしたので原因を調べました。

 --------------------- Disk Space Begin ------------------------ 

 Filesystem                               Size  Used Avail Use% Mounted on
 /dev/mapper/centos_tk2--212--15517-root   28G   14G   15G  48% /
 devtmpfs                                 487M     0  487M   0% /dev
 /dev/vda1                                497M  291M  207M  59% /boot

やったこと

以下コマンドで誰がどれだけ容量を占めているか確認する。

cd /
sudo  du -sh ./*
(略)
4.0K    ./tmp
3.8G    ./usr
8.6G    ./var

なるほど、var配下が怪しそうです。多分wwwかログでしょうね。

cd /var
sudo  du -sh ./*
(略)
7.2G    ./log

あっ…。

原因

mongodbのログが7GB超えてた。
そういえば、今まで特にトラブルなかったからmongoのログ見たこと無いんですよね。やばい。以下のドキュメントを見てローテートの設定を行いました。
Rotate Log Files — MongoDB Manual 3.2
あとは定期的に古いログを圧縮すればOKですね。
おかげでストレージ利用率27%まで落とせました。良かったです。

Let’s Encryptでsplapiをhttp/2に対応させた。

retrorocket.bizは強制的にhttpsで通信するようになっています。retrorocket.bizで試しに作ってるWebアプリがあって、そこから適当なAPIを呼ぼうと思ってJavaScriptからsplapiを呼んだらエラーになって自爆しました。SSL対応してないから当然ですね。
時代は常時SSLだし自爆したのでいい加減対応することにしました。
最初StartSSLで証明書を取得しようとしたのですが、手続きが心底めんどくさいのと、返事がめちゃくちゃ遅いのでLet's Encryptでさっさと証明書を取得することにしました。

証明書の取得は以下のサイトを参考にしました。gitとPythonが必須です。CentOS 7では特に問題なく動作しました。
Let’s Encrypt サーバー証明書の取得と自動更新設定メモ | あぱーブログ
splapiはリバースプロキシにnginxを使っているので、webrootを使用しました。
nginx用のオプションもあるらしいのですが、指定するとnginx.confを書き換えるとかいうとんでもない仕様だったので自前で設定します。

nginxのほうですが、/.well-known/acme-challenge/は403を返すと認証がこけたので、404を返すように指定しました。

        location ^~ /.well-known/acme-challenge/ {
                root /var/www/acme;
        }

        location = /.well-known/acme-challenge/ {
                return 404;
        }

証明書の取得用に実行したコマンドは以下。

./certbot-auto certonly --webroot -w /var/www/acme -d splapi.retrorocket.biz

/.well-known/acme-challenge/配下に認証用のファイルを作って、 http://ドメイン名/.well-known/acme-challenge/hogehoge にアクセスし、認証が終わったらファイルを消す、という動作をします。
/etc/letsencrypt/live/配下にシンボリックリンクが作成されるので、あとはssl_certificateとかに作成された証明書のパスを設定するだけでOKです。
nginx再起動まで5分もかからずに終わったのでめちゃくちゃ爆速ですね。

ssllabsの判定はAでした。
結構きつめの設定にしてるのと、Let’s Encryptへの対応状況がよくわからないので使用しているライブラリによってはSSLでAPIが呼べないかもしれません。
少なくともChrome52でJavaScript実行した時にエラーが出ないので私はこれでいいです。

Bash on Ubuntu on WindowsでMojolicious::LiteとかNet::Twitter::Liteを動かす。

Windows 10 Anniversary Update(Windows 10 バージョン 1607)を適用したので、Bash on Ubuntu on Windowsを使ってみました。念には念を入れて、アップグレードツールを使わずにWindows Updateから適用できる段階でアップデートしたのですが、失敗してOSごと起動しなくなったりしました。相変わらずですね。
私の環境の場合、MacTypeとセキュリティソフトとタッチパッドのドライバが原因だったので、セーフモードで無理やり起動させて原因のアプリを停止させた後、ツールからアップデートしたらうまくいきました。
自分のマシンだとWindows Helloは予想通りサポートされなかったので、使いたかった機能がbashだけになりました。
OSビルドは14393.51です。

Tech TIPS:Windows 10のLinux/Ubuntu互換環境でbashを使う – @IT
ここを見ながらbashを使えるようにしてみました。ちなみに、私の環境だと、bashを使うときにコマンドプロンプトのオプションから「従来のコンソールを使う」のチェックボックスを外さないと動きませんでした。
Bash on ubuntu on Windowsが起動できません。 – マイクロソフト コミュニティ

sudoすると、「名前解決が出来ません」と言われてしまうので、/etc/hostsに「127.0.0.1 ホスト名」を追加しておきました。
ちなみにPerlはv5.18.2でした。メイン環境のさくらVPS/CentOS 7環境がv5.16.3なので、ubuntuのほうがバージョン上ですね。
ubuntuのバージョンは以下のとおり。

➜  ~ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.5 LTS"

ちなみにoh-my-zsh使ってみたのですが、(やっぱり)文字化けしちゃいました、

ホームディレクトリの実体は以下にありました。

C:\Users\ユーザ名\AppData\Local\lxss\home\ubuntuのユーザ名

cpanでモジュールを導入しようとした

最初はcpanでMojoliciousを入れようとしたのですが、「Warning: the following files are missing in your kit」みたいな感じで全然makeできないので、cpanは使えませんでした。cpanmならいけるかもしれないけどめんどくさいので試してません。

apt-getで導入する

ubuntuだとapt-getでperlのモジュールを導入できるので、sudo apt-getで導入してしまいました。

➜  ~ sudo apt-get install libmojolicious-perl
➜  ~ sudo apt-get install libnet-twitter-lite-perl

無事導入できました。

Mojolicious:Liteを動かしてみる

以下のスクリプトをhypnotoadで実行して、http://localhost:8181にアクセスしてみました。

#!/usr/bin/perl

use strict;
use warnings;
use utf8;

use Mojolicious::Lite;
app->config(
  hypnotoad => {
    listen => ['http://*:8181'],
    },
);

get '/' => sub {
  my $self = shift;
  return $self->render(json =>{test => "hello world"});
} => 'index';
app->start;

bashから動かすMojoLite

bashから動かすMojoLite


わーい動いた。

Net::Twitter::Liteを動かしてみる

以下のスクリプトを動かしてみました。

#!/usr/bin/perl

use strict;
use warnings;
use utf8;

use Net::Twitter::Lite::WithAPIv1_1;
use Encode;

my $nt = Net::Twitter::Lite::WithAPIv1_1->new(
consumer_key => $consumer_key,
consumer_secret => $consumer_key_secret,
access_token => $access_token,
access_token_secret => $access_token_secret,
ssl => 1
);

my $tl = $nt->user_timeline();

for my $status(@$tl){
    print encode_utf8($status->{text} ."\n");
}

コンソールから実行してみる

コンソールから実行してみる


わーい動いた。

試したけどできなかったこと

mongodb3は導入できませんでした。以下のStackOverflowの記事と同じ症状です。
Windows 10 Linux Subsystem. How to install MongoDB – Stack Overflow

感想

VirtualBoxでやれ。
ちなみにrm -rfしたらどうなるのかなーと思ったら、やっぱり試してる人がいて「あっ…」てなりました。
`rm -rf /` on Bash on Ubuntu on Windows – Qiita