ExpressでもLet's Encryptしたい

# ExpressでもLet's Encryptでhttps通信をしたい
## 対象
httpsで公開するために無料でサーバー証明書を発行してくれるLet's Encryptを利用する際、公式ページを含めapacheやnginxの設定事例は数多く発見できます。
ですが、お手軽サイトはわざわざnginxをたてるまでの必要もない、Expressでサクッと公開してサクッとhttps化できればいい、という方が対象です。

実は、単なる[letsencrypt-express](https://github.com/Daplie/letsencrypt-express)の紹介で、なにも目新しいものはありません・・・

## 環境
CentOS 7.2
Node v5.9.1
Express v4.13.4
ですが、それほど環境は選ばないと思います。

## 導入
### letsencrypt-expressとは？
公式では[certbot](https://certbot.eff.org/)を導入して・・・というのが本筋ですが、node.jsでexpressを使用するのであれば、[letsencrypt-express](https://github.com/Daplie/letsencrypt-express)という素晴らしいツールが公開されているので、それを利用します。

このletsencrypt-express、以下の利点があります。

- 自動でLet's Encryptへの登録をしてくれます
- Let's Encryptは90日で期限が切れるため、cron等で自動再認証のプロセスをしてくれますが、この更新作業も自動でしてくれます
- vhost / virtual hostingを自動でしてくれます

といった特徴があります。特に、自動更新をしてくれるならラクチンです。

### letsencrypt-expressのインストール

```bash
npm install --save letsencrypt-express
```
で一発です。

### インストール後、expressコードへの埋め込み
下記のごく単純なexpress用のコードをサンプルとしていじっていきます。

```js
var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.listen(80);
```

ここに、letsencrypt-expressの初期化コードを挿入します。

```js
var express = require('express');

// letsencrypt-express用の初期化コード開始

// 次の行の.testing()は本番環境では外して下さい
var LEX = require('letsencrypt-express').testing();

// 以下の2行は環境に合わせて変更して下さい！
var DOMAIN = 'myservice.example.com';
var EMAIL = 'user@example.com';

var lex = LEX.create({
  configDir: require('os').homedir() + '/letsencrypt/etc'
, approveRegistration: function (hostname, approve) { // leave `null` to disable automatic registration
    if (hostname === DOMAIN) { // Or check a database or list of allowed domains
      approve(null, {
        domains: [DOMAIN]
      , email: EMAIL
      , agreeTos: true
      });
    }
  }
});

// ここまで初期化用コード

var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.listen(80);

```

更に、letsencrypt-expressの実行部を挿入します。

```js
var express = require('express');

// letsencrypt-express用の初期化コード開始

// 次の行の.testing()は本番環境では外して下さい
var LEX = require('letsencrypt-express').testing();

// 以下の2行は環境に合わせて変更して下さい！
var DOMAIN = 'myservice.example.com';
var EMAIL = 'user@example.com';

var lex = LEX.create({
  configDir: require('os').homedir() + '/letsencrypt/etc'
, approveRegistration: function (hostname, approve) { // leave `null` to disable automatic registration
    if (hostname === DOMAIN) { // Or check a database or list of allowed domains
      approve(null, {
        domains: [DOMAIN]
      , email: EMAIL
      , agreeTos: true
      });
    }
  }
});

// ここまで初期化用コード

var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

// ここからlets用の実行部コード
lex.onRequest = app;

lex.listen([80], [443, 5001], function () {
  var protocol = ('requestCert' in this) ? 'https': 'http';
  console.log("Listening at " + protocol + '://localhost:' + this.address().port);
});

```

通常は上のコードで動くはずです。以下は別サンプルで、80番ポートでアクセスしてきたら、443にリダイレクトさせたい場合です。http、spdyモジュールを利用します。`npm install --save spdy`でspdyモジュールをインストールしたあと、


```js
var express = require('express');
var http = require('http');
var https = require('spdy');

// letsencrypt-express用の初期化コード開始

// 次の行の.testing()は本番環境では外して下さい
var LEX = require('letsencrypt-express').testing();

// 以下の2行は環境に合わせて変更して下さい！
var DOMAIN = 'myservice.example.com';
var EMAIL = 'user@example.com';

var lex = LEX.create({
  configDir: require('os').homedir() + '/letsencrypt/etc'
, approveRegistration: function (hostname, approve) { // leave `null` to disable automatic registration
    if (hostname === DOMAIN) { // Or check a database or list of allowed domains
      approve(null, {
        domains: [DOMAIN]
      , email: EMAIL
      , agreeTos: true
      });
    }
  }
});

// ここまで初期化用コード

// リダイレクト用
function redirectHttp() {
  http.createServer(LEX.createAcmeResponder(lex, function redirectHttps(req, res) {
    res.setHeader('Location', 'https://' + req.headers.host + req.url);
    res.statusCode = 302;
    res.end('<!-- Hello Developer Person! Please use HTTPS instead -->');
  })).listen(80);
}

// 通常の443接続
function serveHttps() {
  var app = express();

  app.use('/', function (req, res) {
    res.end('Hello World!');
  });

  https.createServer(lex.httpsOptions, LEX.createAcmeResponder(lex, app)).listen(443);
}

redirectHttp();
serveHttps();

```

・・・サンプルそのまんまです・・・

### 実行
あとは`node app.js`等で実行すれば、自動でサーバー証明書をダウンロードの上、サーバーを立ち上げてくれます。
CentOS7の場合はfirewallを使用していると思うので、httpsを許可していなければ、事前に

```bash
$ firewall-cmd --permanent --zone=public --add-service=https
$ firewall-cmd --reload
```

をお忘れなく。

### まとめ
やっぱり素直にnginx等を利用したほうがセットアップは簡単ですが（設定ファイル書き換えで完了）、なにかの参考になれば。