ルモーリン
ホーム 更新 Perl Sample ランドナー サービス 雑談 コースガイド 鉄ゲタ 自転車 Linux リンク 連絡先

MojoliciousでSSL対応

2019-02-20

概要

  1. サーバー証明書の設定
  2. ポート開放
  3. httpからhttpsへのリダイレクト
  4. 動作確認

サーバー証明書の設定

Apacheで使うサーバー証明書はスーパーユーザーが読めるディレクトリ「/etc/pki/」の中に置いていましたが 一般ユーザーからMojoliciousを起動する都合で、そのユーザーのホームディレクトリの中に置くことにしました。 証明書のファイル名は起動時のオプションに指定できます。 ポート番号を10443にしている点は後述。
perl script/myapp daemon -m production -l "https://*:10443?cert=pki/my_ssl.cert&key=pki/my_ssl.key&ca=pki/my_ssl_intermediate.cert"
でも起動時のコマンドラインで指定すると他のプロセスで「PS -FA」を実行できると見られてしまうので避けたい(もうバレてるけど!)。 そこでスクリプト「script/myapp」の中で追加することにしました。 ついでにTLS Ver1.2を使うようにします。 オプションの読み出しはstart_appで行いますから、それまでに@ARGVに追加します。
my $port_tls = "10443";
push @ARGV,
	"-l",
	"https://*:$port_tls?" .
	join "&", qw |
		cert=pki/my_ssl.cert
		key=pki/my_ssl.key
		ca=pki/my_ssl_intermediate.cert
		version=TLSv1_2
		ciphers=EECDH+HIGH:EDH+HIGH:HIGH:MEDIUM:+3DES:!ADH:!RC4:!MD5:!aNULL:!eNULL:!SSLv3:!SSLv2:!LOW:!RSA:!EXP:!PSK:!SRP:!DSS:!KRB5
	|;

# Start command line interface for application
Mojolicious::Commands->start_app("Myapp");
これで起動時オプションのほとんどをスクリプトに移動できシンプルになりました。
perl script/myapp daemon -m production
秘密鍵ファイルにはパスフレーズを設定してありMojoliciousを起動する度にパスフレーズを聞いてきます。 パスフレーズについて検索するとファイルからパスフレーズを外しちゃえというブログを散見しますね。 ちゃんとプログラムがパスフレーズを指定して使いましょうよ。 起動時に1回実行されるところに書きます。 「$passphrase」の中身をハードコーディングしたら身も蓋のないのでそこは別のファイルに入れておきます。
use IO::Socket::SSL;
IO::Socket::SSL::set_defaults(SSL_passwd_cb => sub { return $passphrase; });

ポート開放

SSLのポート番号は443で、一般ユーザー(のプログラム)には待ち受けに使う権限がありません。 ウェルノンポート(well known port=よく知られたポート=1023以下)を使えるのはスーパーユーザー限定の権限です。 1024以上なら一般ユーザーでも使えるので10443を使う事にします。これが登録されていない事をさっき確認しました(おい)。 でもブラウザで普通にアクセスしてくる場合は443を指定してきますからiptablesで443を10443にリダイレクトして繋げるようにします。 設定ファイルは「/etc/sysconfig/iptables」、セクションは「nat」の「PREROUTING ACCEPT」に追加、最後に「service iptables restart」して反映させます。
-A PREROUTING -p tcp -m tcp --dport 443 -j REDIRECT --to-port 10443

httpからhttpsへのリダイレクト

最初からURLの頭が「https://~」であれば問題ありませんけれど以前は「http://~」だった事もあり、ポート80でのアクセスが来る可能性を考慮します。 やはりポート80は使えませんからiptablesでポート80をポート10080へリダイレクトします。 そしてMojoliciousがポート10080に来たリクエストをステータス301でhttpsにリダイレクトします。 まずノーマル(?)の設定をSSLと同様に追加します。
iptablesの設定
-A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-port 10080
script/myapp
my $port_http = "10080";
my $port_tls = "10443";
push @ARGV,
	"-l",
	"https://*:$port_http",
	"-l",
	"https://*:$port_tls?" .
	join "&", qw |
		cert=pki/my_ssl.cert
		key=pki/my_ssl.key
		ca=pki/my_ssl_intermediate.cert
		version=TLSv1_2
		ciphers=EECDH+HIGH:EDH+HIGH:HIGH:MEDIUM:+3DES:!ADH:!RC4:!MD5:!aNULL:!eNULL:!SSLv3:!SSLv2:!LOW:!RSA:!EXP:!PSK:!SRP:!DSS:!KRB5
	|;

# Start command line interface for application
Mojolicious::Commands->start_app("Myapp");
リダイレクトの処理をMyapp.pmのstartupに書きます。 ルーティングの設定の最初にトップレベルのルーティングを定義、underの戻り値で$rを上書きします。 これで全てのリクエストをチェックできます。 本来のhttpsであればis_secureになりunderが元のルーティングを返すのでこれまで通りになります。 httpであればhttpsに書き替えてリダイレクトします。
	# http -> https
	$r = $r->under("/" => sub {
		my $c = shift;

		if ($c->req->is_secure) {
			return 1;
		} else {
			$c->res->code(301);
			$c->redirect_to($c->req->url->to_abs->scheme("https")->to_string);
		}
	});

動作確認

SSLについて、ここでチェックしました→SSL Server Test (Powered by Qualys SSL Labs)
とりあえず「A」貰いました(わーい♪)

リダイレクトについてはこのページのURLの頭にあるhttpsからsを削って表示させてみてください。 結局このページに戻ってくるのでリダイレクトできてることが分かります。