作って学ぶリバースプロキシ④リバースプロキシを経由してサイトアクセスする際にupstreamの仕様でハマった

## これは何?

https://qiita.com/sigma_devsecops/items/6d384b00525a155cff23

前回Lua Scriptについて書いたので今回は実際にLua ScriptとOpenRestyでリバースプロキシを作ってみます。

---

## 環境構築

### OpenRestyのセットアップ

今回はDocker imageをそのまま使いました。

### Lua Scriptのインストール

https://qiita.com/sigma_devsecops/items/6d384b00525a155cff23

前回記事にあるようにhererocksをインストールしてhererocks経由でLuaとパッケージ管理ツールのLuaRocksをインストールしました。

---

## `proxy_pass`を設定してリバースプロキシ経由でアクセスしてみる

`proxy_pass`経由でプロキシされる対象を指定できます。阿部寛のサイトを選んだのは80で動作しており，パケットキャプチャした結果が見やすいからです。

```nginx.conf
        location / {
            root   /usr/local/openresty/reverse_proxy/html;

            # もとのhost = OpenRestyを指定してアクセスされることを確認するためつけておく
            proxy_set_header X-Forwarded-Host $host;
            proxy_set_header Connection "";

            default_type 'text/html';
            proxy_pass http://abehiroshi.la.coocan.jp;
        }
```

この際に`proxy_pass`には以下の仕様があります。

https://qiita.com/sigma_devsecops/items/79d243ac5354c4991d88

[Passing Request Headers](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/)
前記事から抜粋。

> By default, NGINX redefines two header fields in proxied requests, “Host” and “Connection”, and eliminates the header fields whose values are empty strings. “Host” is set to the $proxy_host variable, and “Connection” is set to close.
NGINXではプロキシされるリクエストに対してデフォルトでは以下を実施している。
- HTTPヘッダーの`Host`と`Connection`が上書きされている。
    - `Host`: (`$proxy_pass`に指定したFQDN)`$proxy_host`に書き換わる
  :::note info
  [nginx公式ドキュメント](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#var_proxy_host)に以下の記述がある。
  >`$proxy_host`
  > name and port of a proxied server as specified in the proxy_pass directive;
  
  つまり，`proxy_pass`を設定すると`$proxy_host`が書き換わる
  :::

これのなにがうれしいかというと，localhostでリバースプロキシを起動している際に，自動でHTTPのホストヘッダを書き換えてくれるということです。

Wiresharkでみるとわかりやすいのですが，ブラウザからhttp://localhost:80 にアクセスした場合に最初Hostにはlocalhostが格納されています。
しかし，前述した`proxy_pass`の仕様によりHostがabehiroshi.la.coocan.jpに書き換えられるため，正しくアクセスすることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3718390/46e3d5e7-0995-3156-9b35-b0b3cecb7ff1.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3718390/9d094db3-1dd8-b562-82fc-e2dba9409fca.png)


---

## nginx.confの`upstream`を指定するとうまくいかない

nginx.confにはupstreamというサーバのグループを定義するためのブロックがあります。

```nginx.conf
upstream backend {
    server backend1.example.com       weight=5;
    server backend2.example.com:8080;
    server unix:/tmp/backend3;

    server backup1.example.com:8080   backup;
    server backup2.example.com:8080   backup;
}
```
このブロックはnginx内で名前解決可能です。
そのため，`proxy_pass`にbackendを指定するとupstreamだということを解釈してくれます。

そこで新たに`proxied_server`というupstreamブロックを作り，proxy_passに設定してみました。
すると，404ページにつながるようになってしまいました。

### 原因

upstreamを`proxy_pass`にした場合にはHostヘッダが`proxied_server`になってしまい，404になっているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3718390/9265a2e0-deaf-53fd-6ac9-cc23b3dcda91.png)

### 解決方法

HostヘッダをLuaで無理やり書き換えてやれば良いようです。

#### うまくいった方法

##### 解決策1: LuaでHostヘッダを書き換える。

nginx.confに新しく変数を追加し，書き換えてやります。

```ngix.conf
        location / {
            root /usr/local/openresty/reverse_proxy/html;
            default_type 'text/html';

            access_by_lua_file /usr/local/openresty/reverse_proxy/src/main.lua;

            proxy_pass proxied_server;

            # NOTE: upstreamをproxy_passで指定するとHttp Host Headerがupstrem名になるので，Host名の上書きが必要。
            set $host_header "";
            proxy_set_header Host $host_header;
        }
```

```main.lua
ngx.var.host_header = "abehiroshi.la.coocan.jp"
```

##### 解決策2: upstreamの名前をproxied_serverからfqdn名に変更する

```nginx.conf
    upstream abehiroshi.la.coocan.jp {
        server abehiroshi.la.coocan.jp:80; # テスト用
        keepalive 32;
        keepalive_requests 100;
        keepalive_timeout 10s;
    }
```

#### うまくいかなかった方法

- Luaの中から`ngx.req.set_header()`を使ってHostヘッダを書き換えてみたがだめ。
  - upstreamの中にbalancer_by_lua_fileを定義してluaを実行するパターン
  - main.luaから実行するパターン
- upstreamの中にproxy_set_headerを定義してみたが，構文エラーになる

おそらくですが，proxied_serverというupstreamの名前の付け方にセンスがないのだと思います。
本来upstreamには使用するFQDNを入れるべきでそこにロードバランシングしたいipを書いていくのが正しいのかなと。
当たり前ですが，ロードバランシングする目的がなければupstreamは使わなくて良いなと(今回興味本位で試しましたが)。

---

## 次回

リバースプロキシとして動かすことができたので，次回はBasic認証をかけてみます。

https://qiita.com/sigma_devsecops/items/7726c643b5c65f654d9a