HackMDをnginxで / 以外のlocationで起動する。

# 背景

リアルタイムにmarkdownをプレビューできてGoogle Docsのようにリアルタイムに共同作業できる[HackMD](https://hackmd.io/)が便利なので特定多数しかアクセス出来ない環境に作りたくなった。
Basic認証を掛ければ当面は大丈夫そうだがHackMDにはどうやらその機能がない。

しかし外からアクセスできてサブドメイン無制限なSSL証明書のある環境を持ってないので、SSL設定済みのnginxの下にlocationを掘ってプロキシさせて走らせる事にした。
めんどくさがらずにサブドメイン用の証明書作って解決してもいいのだろうけれど今回は勉強も兼ねている。

HackMDそのものは[Docker-Compose](https://github.com/hackmdio/docker-hackmd/tree/master/hackmd)があるので、Dockerが既に入っている環境なら

```bash:
$ git clone git@github.com:hackmdio/docker-hackmd
$ pip install docker-compose  # 必要に応じて
$ cd docker-hackmd
$ docker-compose up -d
```

とやればバックエンドのPostgreSQLサーバも含めてガサっと立ち上がって3000番ポートで利用可能になるが、こっちだとlocationを変えた場合の設定変更ができない。
(厳密にはスクリプトを書き換えれば何でもできるのだけれどそこまでする元気は無かった)
以下の方法ではDockerを使わずに行う。

# 下準備

前提として Ubuntu14.04でnginxは既に何らかの方法でSSLの設定を済ませているものとする。
nginxにLet's EncryptなどでまともにSSLさせる方法はググると山ほど出てくるのでここには書かない。

ここではそれ以外で必要な環境を揃える。

```bash:
$ sudo apt-get install curl git postgresql-9.3
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh | bash # nvmのインストール
$ source .bashrc  # nvmにパスが通った設定を読み込むため
$ nvm ls-remote   # よさ気なnode.jsのバージョンを探す
$ nvm install v6.2.2 　# 当記事執筆時点での最新版
$ nvm use v6.2.2
$ npm install -g forever  # 後で使う
```

# PostgreSQLの設定

PostgreSQLにhackmdユーザとhackmdデータベースを追加する。
なおユーザ名・パスワード・データベース名は後で作るhackmd用設定ファイルと一致していれば何でも構わない。

```
$ sudo su postgres
$ createuser hackmd
$ psql
postgres=# alter user hackmd password 'hackmdpassword';
postgres=# create database hackmd;
postgres=# grant all on hackmd to hackmd;
postgres=# (Ctrl-dで脱出)
$ (Ctrl-dでpostgresqlユーザから戻る)
```

# HackMDの準備

以下では便宜的に `www.example.com/my-hackmd` 以下にHackMDを立てる前提で書くので各自読み替えて欲しい。

```
$ git clone git@github.com:hackmdio/hackmd
$ cd hackmd
$ npm install -g bower
$ npm install
$ bower install
$ emacs config.json
{
    "production": {
        "domain": "www.example.com",
        "debug": false,
        "urlpath": "my-hackmd",
        "db": {
            "username": "hackmd",
            "password": "hackmdpassword",
            "database": "hackmd",
            "host": "localhost",
            "port": "5432",
            "dialect": "postgres"
        }
    }
}
$ emacs public/js/common.js  # 先頭数行だけ編集する
//common
var domain = 'www.example.com'; // domain name
var urlpath = 'my-hackmd'; // sub url path, like: www.example.com/<urlpath>
//settings
var debug = false;
```

## ちょっとした汚いハック

なお、今回のケースでは HackMDをnginxの下でhttpを吐かせて、https化はnginxが行う。
HackMDが指定するリソースファイルも全部httpsのURLでアクセスさせたいのだが、HackMDが吐くURLをhttpsで始まらせるには`protocolusessl`オプションを指定する必要があり、その場合はHackMDがhttpsを吐こうとしてしまう。
なのでこんな汚い手段に出た。
これはHackMDのバージョンアップでいずれ解消される問題かも知れないので、とりあえず読み飛ばして実行して、ダメだったらこれを実行する程度にして欲しい。

```bash:
$ perl -i -pe"s|var protocol = protocolusessl ? 'https://' : 'http://';|var protocol = 'https://';|" lib/config.js;
```

強引にURL生成に使う文字列にhttpsと埋め込んでいる。
もしくはこんなことをせずに素直にhackmdにhttpsを吐かせてそれをルーティングすべきかも知れない。nginxにhttpsクライアントをさせるのはちょっと好かないが。

## HackMDサーバ起動

```bash:
$ NODE_ENV='production' forever start app.js
```

これで `localhost:3000` でnode.jsが起動するが、これに443ポートに居るnginxから中継させてアクセスさせたい。

# nginxの設定

ユーザから来たアクセスを中継して localhostで走っている node.jsのhackmdに取り次ぐ設定を書く。

```bash:
$ sudo emacs /etc/nginx/conf.d/hackmd.conf
upstream hackmd {
    server 127.0.0.1:3000;
}

server {
    listen       443;
    server_name  www.example.com;
    location /my-hackmd {
        auth_basic "Restricted";
        auth_basic_user_file  /path/to/htpasswd/file/.htpasswd;
        rewrite /my-hackmd/(.*) /$1 break;
        proxy_pass         http://hackmd;
        proxy_redirect     default;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header   X-Forwarded-For $remote_addr;
        proxy_set_header   Host $http_host;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
$ sudo service nginx restart
```

Basic認証を掛けたいので [Nginx で Basic 認証](http://qiita.com/kotarella1110/items/be76b17cdbe61ff7b5ca)を参考にした。
そのあたりの設定が

```
        auth_basic "Restricted";
        auth_basic_user_file  /path/to/htpasswd/file/.htpasswd;
```

nginxは `/my-hackmd/foo/bar` 宛に来たアクセスのURLを読み替えて `127.0.0.1:3000/foo/bar` 宛にアクセスを試みて結果を中継する必要がある。
そのあたりの設定が

```
        rewrite /my-hackmd/(.*) /$1 break;
        proxy_pass         http://hackmd;
        proxy_redirect     default;
```

HackMDは `socket.io` を使っているので、WebSocket通信のupgradeも中継する必要があって少し設定が長くなっている。 
そのあたりの設定が

```
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header   X-Forwarded-For $remote_addr;
        proxy_set_header   Host $http_host;
```

実際、冗長な物があるかも知れないのでいくつか消しても動くかも知れない。がわざわざ試す気にならない。

さて、 https://www.example.com/my-hackmd/ にアクセスしてみてBASIC認証用の小窓が出てくれば成功。
豊かなHackMDライフを送ろう。