Discourseをホスト側でProxyする

立ち上げてみたらかなりかっこよく動きそうに見えたDiscourse、とはいえ他のサービスも調べたいので試験環境に同居させたいと考えたが、失敗していた。

考え方としては、Dockerで構築された環境の中にあるNGINXの出入り口を unix domain socket に変えて、そこへProxyすれば良いのかな…と思うんだけど。
Discourse / Running other websites on the same machine as Discourse





やること。

 

環境

VMwareでUbuntu 18.04を立ち上げ、そこにdiscourseを導入
既に動作している前提。

色々試してキャッシュされるせいか、Chromeでテストすると見えないはずのページが見えちゃったりするので、シークレットモードでテストしてみている

コンテナの設定変更

コンテナの内容を変更する。

やっているのは、sslをやめること、unix domein socketを導入すること、ポートを使用しないようにすること。

/opt/discourse/containers/app.yml
…
templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Uncomment these two lines if you wish to add Lets Encrypt (https)
# - "templates/web.ssl.template.yml"
# - "templates/web.letsencrypt.ssl.template.yml"
  - "templates/web.socketed.template.yml"
…
expose:
# - "80:80"   # http
# - "443:443" # https
…

再構築。

./launcher rebuild app

 

NGINXでProxy設定

まず、ちゃんと動くのはこれ、という環境を作って確かめてからApacheに差し替えることを考えようと思った。インストール直後に色々試したときに、CSRFの問題に悩まされたから。

設定ファイルの作成

Proxy設定のconfファイルを作成。

/etc/nginx/sites-available/discourse
server {
    listen 80; listen [::]:80;
    server_name hogeserver.hogeddns.jp;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;  listen [::]:443 ssl http2;
    server_name hogeserver.hogeddns.jp;

    ssl_certificate     /etc/ssl/private/temp.hogeserver.hogeddns.jp.crt;
    ssl_certificate_key /etc/ssl/private/temp.hogeserver.hogeddns.jp.key;
#   ssl_dhparam         /var/discourse/shared/standalone/ssl/dhparams.pem;
    ssl_session_tickets off;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;

    http2_idle_timeout 5m; # up from 3m default

    location / {
        proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

 

サイトの有効化と反映

デフォルトのconfを無効化し、作ったconfを有効化。
NGINXで設定を再読み込み。

# rm /etc/nginx/sites-enabled/default
# ln -s /etc/nginx/sites-available/discourse /etc/nginx/sites-enabled/discourse
# systemctl reload nginx

これで動いた。NGINXを使うならこれで問題なさそう。

ApacheでProxy設定

NGINXの設定を見ると、Apacheではちょっとやり方が違っていた。

設定ファイルの作成

NGINXの設定をまねして作ってみる。

<VirtualHost *:80>
    ServerName temp.hogeserver.hogeddns.jp
    ServerAdmin webmaster@hogeserver.hogeddns.jp
    Redirect permanent / https://temp.hogeserver.hogeddns.jp/
</VirtualHost>

<VirtualHost *:443>
    ServerName temp.hogeserver.hogeddns.jp
    ServerAdmin webmaster@hogeserver.hogeddns.jp

    ErrorLog  ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    SSLProxyEngine on
    ProxyPreserveHost on
    ProtocolsHonorOrder on
    RequestHeader set Host "temp.hogeserver.hogeddns.jp"
    Protocols http/1.1
    RemoteIPHeader X-Forwarded-For
    RequestHeader set X-Forwarded-Proto "https"
    RemoteIPHeader X-Real-IP
    ProxyPass / unix:/var/discourse/shared/standalone/nginx.http.sock|http://temp.hogeserver.hogeddns.jp/

    SSLEngine on
    SSLCertificateFile    /etc/ssl/private/temp.hogeserver.hogeddns.jp.crt
    SSLCertificateKeyFile /etc/ssl/private/temp.hogeserver.hogeddns.jp.key
</VirtualHost>

 

サイトの有効化と反映

デフォルトのconfを無効化し、作ったconfを有効化。
必要なモジュールを追加してApacheを再起動。

# a2dissite 000-default.conf
# a2ensite discourse.conf
# a2enmod proxy proxy_http proxy_https ssl headers remoteip
# systemctl restart apache2

これで、動き出した。

再起動したときにDiscourseを自動起動させる

再起動してみたらDiscourseが自動起動しなかった。
色々と設定を変えている中で何かが起きたのかもしれない。
Qiita / Dockerコンテナ単体で自動起動の設定と確認を行う

# docker update --restart=always app

これで再起動時にDiscourseが自動起動するようになった。

やったこと

unix domain socket

コンテナの外部にNGINXをインストールしてそれを使おうとするなら、どうもすぐにできそうな気配。

そのときには、こんな感じでProxyするみたい。

proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;

unix:<ソケット名>: という表現が何を指しているのかよく分からず、探してみた。
Qiita / h2oとUnixドメインソケット

凄く早いらしい。

どうも、こんな感じで書くらしい。
stackoverflow / Apache: proxy to unix socket named in URL

RewriteEngine On
RewriteRule      /(.*)/(.*)  http://%{HTTP_HOST}/$2    [P]
ProxyPass        / unix:/var/sockets/$1.sock|http://%{HTTP_HOST}/
ProxyPassReverse / unix:/var/sockets/$1.sock|http://%{HTTP_HOST}/

$0~$9はRewriteRule backreferencesで、ルールにおいて括弧でグループ化された部分を表す。$0はマッチした文字列全体を表し、$1は1つめ、$2は2つめを表す。

ちなみに%0~%9はRewriteCond backreferencesで、後は一緒。

この例だと、RewriteRuleの1つめの(.*)に一致するところが$1に置き換えられてソケットファイル名が確定、そこに今回だと… http://temp.hogeserver.hogeddns.jp/として渡されるっぽい。

Apacheのバージョン確認

インストール前にパッケージバージョンを確認。unix domain socketに対応しているのかどうかを確認したくなったから。
うまいぼうぶろぐ / apache 2.4.7 からProxyPassでunix domain socketを指定できるようになってた

# sudo apt show apache2
Package: apache2
Version: 2.4.29-1ubuntu4.13
Priority: optional
Section: web
Origin: Ubuntu
…

使えそう。

NGINXのヘッダー操作をまねる

どんな情報が渡せれば動くのだろうか…ということで、それぞれについて調べてみた。

proxy_set_header Host $http_host;

恐らく、ヘッダーに$http_hostが追加されるんだろうけど、$http_hostってなんだろ?
Qiita / proxy_set_header Hostで設定するNginxの変数一覧

リクエストヘッダにあるHTTP_HOSTの値、と書かれている。

Apacheでいうと、mods_headersのRequestHeaderディレクティブで、設定値はServerNameで指定した値を設定すれば良さそう。

proxy_http_version 1.1;

ここに書かれている。
hacknote / httpsにおけるhttpのバージョン選択(apache)

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

ここに書かれている。
とあるサーバエンジニアの備忘録 / Apacheアクセス制御をロードバランサー配下でも

proxy_set_header X-Real-IP $remote_addr;

ここで書かれている。
Stackoverrun / Apacheは[REMOTE_ADDR]を特定できません

proxy_set_header X-Forwarded-Proto https;

ここで書かれている。
Qiita / Apach, NginxをプロキシにしたRailsアプリでhttpsがリダイレクト時にhttpになる問題

unix domain socketの通信内容をダンプする

こうして調べて、多分これだろうというものを設定したけれども、CSRF問題を解消できない。この問題が発生する理由はヘッダーがちゃんと設定できていないからなんだけれども、どこが設定できていないのか分からない…。

通信内容をダンプしてみたいと思ったら、この記事を発見。
Qiita / UNIXドメインソケット通信の内容を見たい

プロセスにアタッチする必要があるようなので、プロセスIDを見てみる。

# ps aux | grep nginx
root       2624  0.0  0.0   2160   680 ?        Ss   21:37   0:00 runsv nginx
root       2630  0.0  0.3  54024  7100 ?        S    21:37   0:00 nginx: master process /usr/sbin/nginx
www-data   2711  0.0  0.2  55056  5176 ?        S    21:37   0:00 nginx: worker process
www-data   2712  0.0  0.2  55068  5288 ?        S    21:37   0:00 nginx: worker process
www-data   2713  0.0  0.1  54572  3840 ?        S    21:37   0:00 nginx: cache manager process
root       9820  0.0  0.0  11332  1100 pts/0    D+   22:49   0:00 grep --color=auto nginx

試してみたところ、1つめのworker processにアタッチすると通信内容が見えた。
色々な環境があるだろうから、一概には言えないけど。

# strace -s 1024 -f -p 2711

※プロセス番号は都度変わるので、そのとき調べた結果で置き換え。

で、ApacheとNGINXの違いを調べてみた。

■Apache
recvfrom(13, "GET /login HTTP/1.1\r\nHost: temp.hogeserver.hogeddns.jp\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-Mode: same-origin\r\nSec-Fetch-Dest: empty\r\nReferer: https://temp.hogeserver.hogeddns.jp/auth/failure?message=csrf_detected\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: ja,en-US;q=0.9,en;q=0.8\r\nX-Forwarded-Prot: https\r\nX-Forwarded-For: 192.168.33.33\r\nX-Forwarded-Host: temp.hogeserver.hogeddns.jp\r\nX-Forwarded-Server: temp.hogeserver.hogeddns.jp\r\nConnection: Keep-Alive\r\n\r\n", 1024, 0, NULL, NULL) = 732

■NGINX
recvfrom(13, "GET /session/csrf HTTP/1.1\r\nHost: temp.hogeserver.hogeddns.jp\r\nX-Forwarded-For: 192.168.33.33\r\nX-Forwarded-Proto: https\r\nX-Real-IP: 192.168.33.33\r\nConnection: close\r\ndiscourse-track-view: true\r\naccept: application/json, text/javascript, */*; q=0.01\r\ndiscourse-present: true\r\nx-csrf-token: undefined\r\nx-requested-with: XMLHttpRequest\r\nuser-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36\r\nsec-fetch-site: same-origin\r\nsec-fetch-mode: cors\r\nsec-fetch-dest: empty\r\nreferer: https://temp.hogeserver.hogeddns.jp/\r\naccept-encoding: gzip, deflate, br\r\naccept-language: ja,en-US;q=0.9,en;q=0.8\r\n\r\n", 1024, 0, NULL, NULL) = 646

ん!?スペルミス発見…俺は何をやっているんだ…。

これを修正したところ、動き出した。

さいごに

今まで苦手にしていて手を出さなかった領域。通信内容がどうなっていて、どこが問題になっているのか、というのをはじめてちゃんと見た気がする。

書いてあるとおりに設定しても動かない、というのは実はいつも間違いで、何か読み違えたり考え違いしたりしている。最終的に何がどうなっていれば良いのか、というのを見てみたかったのだが、今回のこれは見る範囲の狭さもあってちょうど良い題材だった。

でも…ますますマニアックな世界にはまり込んでいる気もする。

お気軽にどうぞ ~ 投稿に関するご意見・感想・他

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です