nginx でポート範囲指定した UDP proxy を建てるメモ

## 背景

* TCP に加えて, UDP ポートを forward したい(proxy). ポート範囲(複数ポート)指定して.
* proxy だけほしい + 8000 番代以降のポートでよいのでユーザー権限で動かすで OK

## 情報

UDP通信のリバースプロキシサーバを立てる
https://qiita.com/trajanme/items/55975e37e9a6b12ca71e

NGINX を使った TCP/UDP Proxy の構築
https://tech-lab.sios.jp/archives/10313

nginxによるTCPロードバランサー
https://engineering.mercari.com/blog/entry/2016-08-17-170114/

ありがとうございます.

古い nginx だと udp には対応していない + ポート範囲指定できないので注意です!

最低限 v1.15.10 が必要です.

今回は 1.19.10 を使います.

Proxy 機能使うにはソースからビルドしないといけませんでした.
(最近の Ubuntu パッケージとかだとモジュール(動的ロード)でいけるかもですが)

## nginx のビルド

configure で, 古きつらき autoconf configure かと思いきや自前 bash script でさらにつらいです(本当にやめてほしい) :cry: :cry: :cry: :cry: :cry:
 
`--with-stream` でコンパイルします(デフォルトだと静的リンク). `--with-stream=dynamic` でモジュールを動的リンクというのもできますが, あまり利点はないでしょう.

pcre, zlib 関連は不要なので off(`--without-***`)でよいです.


## ユーザー権限でうごかす

`-p` で設定ファイルのフォルダ指定してやります.
mime.types とかのファイルは適宜コピってきましょう.

```
$ nginx -p <path/to/conf>
```

ログを stdout に出すモードほしいですが無いっぽい?

`-t` で conf ファイルとかチェックできます

stream とかで指定するパスが wildcard 指定の場合, ファイルパスが間違っていてもエラー出さないので注意です!

設定例はこんな感じです.

```
#user  nginx;
worker_processes  1;
pid logs/nginx.pid;

events {
  worker_connections  1024;
}

http {
  include mime.types;
  default_type  application/octet-stream;

  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
  access_log  logs/access.log  main;
  sendfile  on;
  keepalive_timeout 65;

  include /home/syoyo/local/nginx/conf.d/*.http.conf;
}

stream {
  include /home/syoyo/local/nginx/conf.d/*.stream.conf;

  # wildcard 使う場合, 存在しないパスでもエラー出さないので注意!
  include /nonexit/syoyo/local/nginx/conf.d/*.stream.conf;
}
```

proxy.stream.conf

```
upstream backend {
    server 10.8.0.6:8089;
}

server {
    listen       8089 udp;
    proxy_pass   backend;
}
```

netcat, socat で通信試してみます.

```
# client(10.8.0.6)

$ socat UDP4-LISTEN:8089 stdout
```


```
# proxy(10.8.0.1)
$ nc -u 0.0.0.0 8089
hello
```

client 側に hello と表示されれば成功です!

うまく行かない場合, client と proxy は同一ネットワークにあり, firewall(CentOS の場合基本ブロックなのでポート開放しないといけない)でブロックされていないことも確認しておきましょう.

### 複数ポートのフォワーディング


https://serverfault.com/questions/279262/nginx-proxy-a-large-port-range-to-equivalent-port-on-a-different-ip-address

v1.15.10 からポートを範囲で指定できるようになっています!

ただ, `upstream` と `server` で両方指定するのはダメで,

```
upstream backend {
    server 10.8.0.6:8089-8099;
}

server {
    listen       8089-8099;
    proxy_pass   backend;
}
```

以下のように inline で記述(`$server_port` 変数)しないとダメでした.

```
# OK
server {
    listen       8089-8099;
    proxy_pass   10.8.0.6:$server_port;
}
```

`upstream` に変数かなにかで指定できるかもしれませんが, いかんせん nginx のドキュメントがひどくてなにも書かれていませんので諦めました. 

とりあえず inline 表示でうまく forward できるのでよしとします.
(ロードバランスしたいときはソースコードがんばって読んで設定できるか調べてね :kissing: )

TCP, UDP ではそれぞれ別で設定が必要です.

```
server {
    listen       8089-8099;
    proxy_pass   10.8.0.6:$server_port;
}

 
server {
    listen       8089-8099 udp;
    proxy_pass   10.8.0.6:$server_port;
}
```

## TODO

* [ ] nginx, とりあえず UDP forward できるのは確認しましたが, 設定ファイルフォーマット独自だしソースコードはロシアンな C コードでつらいので他のツールに一刻も早く乗り換えたい. SOCKS5 proxy?
  * SOCKS4 は TCP だけ.
  * iptables or STUN がいいのかしら? :thinking:
  * Envoy?  https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/udp_filters/udp_proxy
  * 自作とか?

### さらなる高みへのメモ

そして壁の向こうへ。 NAT/Firewallを越えて通信しよう―WebRTC入門2016
https://html5experts.jp/mganeko/20618/

gost - GO Simple Tunnel がすごい
https://qiita.com/tongari0/items/84f630483bef19a2e386

プロキシとの戦いに疲れたのでgoで透過プロキシを作ってみた
https://qiita.com/wadahiro/items/7fa852b7217940e6ef26