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 でさらにつらいです(本当にやめてほしい)

--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 の場合基本ブロックなのでポート開放しないといけない)でブロックされていないことも確認しておきましょう.

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

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

ただ, upstreamserver で両方指定するのはダメで,

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 できるのでよしとします.
(ロードバランスしたいときはソースコードがんばって読んで設定できるか調べてね )

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

さらなる高みへのメモ

そして壁の向こうへ。 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