このブログのSSL設定とかの話。

前Cipher Suitesの話をしたのでこのブログのSSL設定を紹介します。Webサーバはnginxです。
試験的にtag.retrorocket.bizにだけh2oを使ってるのですが、なかなかいい感じなのでそのうち乗り換えるかもしれません。
サブドメインで運用しているサービスは一部設定が足りてなかったりしてSSL LabsでA+じゃなかったりするのですが、AかA-なので及第点と思っています。retrorocket.bizはA+です。
SSL Server Test (Powered by Qualys SSL Labs)
http/2には対応済みです。

設定の一部

ssl_dhparam /etc/nginx/ssl/dhparam.pem;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;

add_header Strict-Transport-Security max-age=15768000;

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/ca-certs.crt;

resolver 8.8.8.8 8.8.4.4 valid=300s;

設定の根拠

基本的に「瀕死のリテラシー メカニカルに殺す」「世の中のいけてるところが推奨しているものは信頼できるもの」、という考えなので、MozillaWikiのIntermediate compatibilityの設定を使用しています。
Security/Server Side TLS – MozillaWiki
Modernの設定に寄せたいので、TLS1.0は切っていいかなと思ってアクセスログを見たのですが、利用者にAndroid4系の人がめちゃくちゃ多いので諦めました。
openssl 1.1.0以上を使用しているとCHACHA20-POLY1305が使用できます。
新しいTLSの暗号方式ChaCha20-Poly1305 – ぼちぼち日記

参考にしたサイト等

自分で理解しないまま設定するのは絶対避けたいので、以下のサイトを参考にしています。
理解してるつもりの SSL/TLS でも、もっと理解したら面白かった話 · けんごのお屋敷
httpsだからというだけで安全?調べたら怖くなってきたSSLの話!? – Qiita
我々はどのようにして安全なHTTPS通信を提供すれば良いか – Qiita
OCSP StaplingはMozillaWiki見るまで知らなかったので勉強になりました。

Start SSLの証明書で運用しているサービスはそろそろ証明書更新の時期なので、Let’s Encryptに乗り換えようと思っています。

LINE::Bot::APIとIRKitで部屋の家電を制御するサンプルコード。

解説書いてる時間がなかったのでサンプルコードだけ貼っておきます。そのうち書く。

LINEのmessage apiを使おうと思ってuse LWP::UserAgentとuse JSONでゴリゴリ書いてたら普通にPerlのSDKがあったときの顔をしていました。SDK充実しててすごい。
line/line-bot-sdk-perl: LINE: :API – SDK of the LINE Messaging API for Perl

私はWebhookのcallback先を自宅サーバ(Raspberry Pi 2)に設置してるのでIRKit Device HTTP APIを使用できます。というかInternet HTTP API使うのめんどくさいから自宅サーバをcallback先にしたというのもあります。
IBM BluemixとかHerokuとか使ってる人はInternet HTTP APIを使ってください。
アプリはhypnotoadとMojolicious::Liteで動かしていて、リバースプロキシに(無駄に)h2oを使ってるのですが、あまり意味は無いですね。

#!/usr/bin/perl

# 2017/01/26 ちょっと書き直した

use utf8;
use strict;
use warnings;

use LINE::Bot::API;
use LINE::Bot::API::Builder::SendMessage;
use LINE::Bot::API::Builder::TemplateMessage;
use LWP::UserAgent;
use Mojolicious::Lite;

my $ua = new LWP::UserAgent();

my $CHANNEL_ACCCESS_TOKEN = "xxx"; 
my $CHANNEL_SECRET="xxx";

# API認証情報
my $bot = LINE::Bot::API->new(
    channel_secret       => $CHANNEL_SECRET,
    channel_access_token => $CHANNEL_ACCCESS_TOKEN,
    );

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

# 当初いろんなリクエストを送る予定だった
sub call_api(){
  my $url = shift;
  my $method = shift;
  my $headers = shift;
  my $content = shift;

  my $req = HTTP::Request->new($method, $url);
  while (my ($key, $value) = each(%$headers)){
    $req->header($key => $value);
  }
  $req->content($content);
  return $ua->request($req);
}

# IRKitのAPIを呼び出す
sub call_irkit(){
  my $content = shift;
  my $IRKIT = "http://192.168.10.150/messages";
  my %IRKIT_H = ("X-Requested-With" => "curl");
  return &call_api($IRKIT, "POST", \%IRKIT_H, $content);
}

get '/' => sub {
  my $self = shift;
  return $self->render(template => 'index', format => 'html');
} => 'index'; #別になくても良い

post '/callback' => sub {
  my $self = shift;

  # LINEから受信したリクエストボディ
  my $source = $self->req->body;
  # シグネチャ検証
  unless ($bot->validate_signature($source, $self->req->headers->header('X-Line-Signature')) ) {
    return $self->render(json => {'status' => "failed to validate signature"});
  }

  my $events = $bot->parse_events_from_json($source);
  my $event = ${ $events }[0]; #多分先頭のイベントしか使わないので
  unless($event->is_message_event && $event->is_text_message){
    return $self->render(json => {'status' => "not text event"});
  }

  # 自分以外の人が操作できないようにする
  unless($event->user_id eq "my LINE ID"){
    return $self->render(json => {'status' => "You are not me."});
  }

  my $reply_text = $event->text;
  if($reply_text eq "コマンド教えて"){ # carouselの動作確認も兼ねてヘルプ

    my $carousel = LINE::Bot::API::Builder::TemplateMessage->new_carousel(
      alt_text => 'コマンドを教えます',
    );

    my @commands = ("電気つけて", "電気消して", "エアコンつけて", "エアコン消して");
    for my $i (@commands) { #carouselは5つまで
      my $column = LINE::Bot::API::Builder::TemplateMessage::Column->new(
        title     => "commands",
        text      => "利用頻度の高いコマンドです",
      )->add_message_action(
        label => $i,
        text  => $i,
      );
      $carousel->add_column($column->build);
    }
    my $messages = LINE::Bot::API::Builder::SendMessage->new()->add_template($carousel->build);
    $bot->reply_message($event->reply_token, $messages->build);
    return $self->render(json => {'status' => "200OK"});
  }
  my $messages = LINE::Bot::API::Builder::SendMessage->new;
  $messages->add_text( text => "命令を実行します!" );
  $messages->add_text( text => $reply_text );

  my $commands = {
    "電気つけて" => '{"format":"raw","freq":38,"data":[18031,...]}',
    "電気消して" => ...,
    #etc...,
  };
  # 受け取ったメッセージに対応するJSONをIRKitに送信する
  my $command = $commands->{$reply_text};
  if ($command){
    &call_irkit($command);
  } else { # ここの挙動をヘルプにするのもありだと思います
    $messages->add_text( text => "その命令は受け付けられません!" );
  }

  $bot->reply_message($event->reply_token, $messages->build);
  return $self->render(json => {'status' => "200OK"});
} => 'callback';

app->start;