DEV Community

vast cow
vast cow

Posted on

Exposing Supermicro X11DPI (ATEN IPMI) with Tailscale Serve: Use `socat` or Nginx `stream` Instead of an HTTP Reverse Proxy

While trying to expose the IPMI interface (ATEN-based) on a Supermicro X11 series motherboard using Tailscale Services (tailscale serve --service), I ran into an unexpected pitfall.

The conclusion is straightforward:

When exposing ATEN IPMI through Tailscale Serve, a TCP-level proxy (such as socat or Nginx stream) should be your first choice instead of an HTTP reverse proxy.

What I Wanted to Do

I wanted to expose the IPMI interface on my LAN under a service name like:

https://x11dpi-ipmi.<tailnet>.ts.net/
Enter fullscreen mode Exit fullscreen mode

With Tailscale Services, this can be configured as:

tailscale serve \
  --service=svc:x11dpi-ipmi \
  --https=443 \
  https+insecure://x.x.x.x
Enter fullscreen mode Exit fullscreen mode

However, the HTML5 KVM console did not work correctly.

My First Suspect: WebSockets

The HTML5 KVM console uses WebSockets.

My initial assumptions were:

  • Maybe Tailscale Serve doesn't fully support WebSockets.
  • Maybe the Upgrade header isn't being forwarded correctly.

However, my own aiohttp WebSocket server worked perfectly through the same setup.

That meant there was nothing inherently wrong with the combination of:

  • Tailscale
  • WebSockets
  • The browser

The Actual Cause

After inserting Nginx as an HTTP reverse proxy for debugging, I found this error:

upstream sent invalid header: "\x20..."
Enter fullscreen mode Exit fullscreen mode

resulting in:

502 Bad Gateway
Enter fullscreen mode Exit fullscreen mode

The request flow looked like this:

Browser
    ↓
Tailscale
    ↓
Nginx HTTP Proxy
    ↓
ATEN IPMI
Enter fullscreen mode Exit fullscreen mode

This indicates that Nginx rejects the HTTP response headers returned by the ATEN IPMI firmware as invalid.

Simple GET requests work, but CGI requests such as:

POST /cgi/ipmi.cgi
Enter fullscreen mode Exit fullscreen mode

fail.

For example:

op=UID_SUPPORT.XML
Enter fullscreen mode Exit fullscreen mode

returns 502 Bad Gateway when sent via POST.

Meanwhile:

GET /cgi/ipmi.cgi
Enter fullscreen mode Exit fullscreen mode

works without issue.

In other words, this is an HTTP protocol compatibility issue.

This Is Not a WebSocket Problem

Since only the HTML5 KVM console initially appeared to fail, WebSockets seemed like the obvious culprit.

In reality, however:

The HTTP parser fails while processing the CGI POST response.

The browser never reaches the stage where WebSocket communication becomes relevant because HTTP communication with the IPMI interface has already failed.

Solution 1: socat (Recommended)

Instead of interpreting HTTP at all, simply forward TCP traffic:

Tailscale
    ↓ TLS termination
TCP
    ↓
socat
    ↓ TLS
IPMI
Enter fullscreen mode Exit fullscreen mode

For example:

socat \
  TCP4-LISTEN:8082,bind=127.0.0.1,reuseaddr,fork \
  OPENSSL:x.x.x.x:443,verify=0
Enter fullscreen mode Exit fullscreen mode

Then configure Tailscale Serve:

tailscale serve \
  --service=svc:x11dpi-ipmi \
  --tls-terminated-tcp=443 \
  tcp://127.0.0.1:8082
Enter fullscreen mode Exit fullscreen mode

With this configuration, everything worked correctly:

  • CGI
  • Cookies
  • WebSockets
  • HTML5 KVM

Because no HTTP headers are parsed, ATEN's non-standard HTTP implementation is passed through unchanged.

Solution 2: Nginx stream

Nginx can achieve the same result by using the stream module instead of the HTTP module.

stream {
    server {
        listen 127.0.0.1:8082;

        proxy_ssl on;
        proxy_ssl_verify off;

        proxy_pass x.x.x.x:443;
    }
}
Enter fullscreen mode Exit fullscreen mode

Since stream operates as a TCP proxy, it never parses HTTP headers.

As a result, it avoids compatibility issues with the ATEN IPMI firmware.

Why I Don't Recommend an HTTP Reverse Proxy

The conventional approach would be something like:

proxy_pass https://x.x.x.x;
Enter fullscreen mode Exit fullscreen mode

However, with ATEN IPMI this can result in:

upstream sent invalid header
Enter fullscreen mode Exit fullscreen mode

This happens before considerations such as:

  • Host headers
  • Origin
  • WebSockets

The HTTP response itself is rejected by Nginx's parser.

Adjusting buffer sizes or disabling buffering (for example, proxy_buffering off) does not resolve the problem.

Why socat Works

socat does not understand HTTP.

It simply forwards TCP streams.

Therefore, even if the ATEN IPMI firmware returns unconventional HTTP responses, socat passes them through unchanged.

Modern browsers are apparently tolerant enough to process those responses successfully.

Conclusion

When exposing an ATEN-based Supermicro X11 IPMI interface through Tailscale Services, my recommended order is:

  1. socat + tailscale serve --tls-terminated-tcp
  2. Nginx stream + tailscale serve --tls-terminated-tcp
  3. Nginx HTTP reverse proxy (not recommended)

For most web applications, an HTTP reverse proxy is the default choice.

ATEN IPMI is an exception.

A TCP-level proxy that forwards traffic without inspecting HTTP is significantly more reliable than an HTTP-aware reverse proxy.

If you encounter errors such as upstream sent invalid header or 502 Bad Gateway, don't assume the problem is with WebSockets or Tailscale. First, check whether you're routing the traffic through an HTTP reverse proxy.

Supermicro X11DPI (ATEN IPMI) を Tailscale Serve で公開するなら、Nginx HTTP Proxy ではなく socat / Nginx stream を使う

Supermicro X11世代のIPMI(ATENベース)を Tailscale Services (tailscale serve --service) で公開しようとしたところ、意外な落とし穴にはまりました。

結論から言うと、

ATEN IPMIを Tailscale Serve 経由で公開する場合は、HTTPリバースプロキシではなく TCPレベルで中継する構成(socat または Nginx stream)が第一選択です。

やりたかったこと

LAN内のIPMIを

https://x11dpi-ipmi.<tailnet>.ts.net/
Enter fullscreen mode Exit fullscreen mode

のようなサービス名で公開したい。

Tailscale Services を使えば、

tailscale serve \
  --service=svc:x11dpi-ipmi \
  --https=443 \
  https+insecure://x.x.x.x
Enter fullscreen mode Exit fullscreen mode

のような構成が作れます。

しかし、実際にはHTML5 KVMが正常に動作しませんでした。

最初に疑ったのはWebSocket

HTML5 KVMはWebSocketを使用しています。

そのため、

  • Tailscale ServeがWebSocketに対応していないのでは?
  • Upgradeヘッダーが落ちているのでは?

と考えました。

しかし、自作の aiohttp WebSocketサーバーでは正常に動作しました。

つまり、

  • Tailscale
  • WebSocket
  • ブラウザ

の組み合わせ自体には問題がありません。

実際の原因

NginxをHTTPリバースプロキシとして挟いて調査すると、

upstream sent invalid header: "\x20..."
Enter fullscreen mode Exit fullscreen mode

というエラーが出ました。

つまり、

502 Bad Gateway
Enter fullscreen mode Exit fullscreen mode

になっています。

これは

ブラウザ
    ↓
Tailscale
    ↓
Nginx HTTP Proxy
    ↓
ATEN IPMI
Enter fullscreen mode Exit fullscreen mode

という構成で、

ATEN IPMIが返すHTTPレスポンスヘッダーをNginxが不正と判断して拒否している

ことを意味します。

GETでは問題なくても、

POST /cgi/ipmi.cgi
Enter fullscreen mode Exit fullscreen mode

のようなCGIでは失敗します。

例えば、

op=UID_SUPPORT.XML
Enter fullscreen mode Exit fullscreen mode

をPOSTすると502になります。

一方、

GET /cgi/ipmi.cgi
Enter fullscreen mode Exit fullscreen mode

は正常に返ります。

つまり、

HTTPレベルの互換性問題です。

これはWebSocketの問題ではない

最初はHTML5 KVMだけが失敗するのでWebSocketを疑いました。

しかし実際には、

CGIのPOSTレスポンスの時点でHTTPパーサが失敗していました。

そのため、

HTML5 KVM以前にIPMIとのHTTP通信自体が成立していません。

解決策1: socat(おすすめ)

HTTPを一切解釈せず、

Tailscale
    ↓ TLS終端
TCP
    ↓
socat
    ↓ TLS
IPMI
Enter fullscreen mode Exit fullscreen mode

という構成にします。

例えば

socat \
  TCP4-LISTEN:8082,bind=127.0.0.1,reuseaddr,fork \
  OPENSSL:x.x.x.x:443,verify=0
Enter fullscreen mode Exit fullscreen mode

そして

tailscale serve \
  --service=svc:x11dpi-ipmi \
  --tls-terminated-tcp=443 \
  tcp://127.0.0.1:8082
Enter fullscreen mode Exit fullscreen mode

とします。

この構成では、

  • CGI
  • Cookie
  • WebSocket
  • HTML5 KVM

すべて正常に動作しました。

HTTPヘッダーを解析しないため、ATEN独自の実装にも影響されません。

解決策2: Nginx stream

NginxでもHTTPではなくstreamモジュールを使えば同様です。

stream {
    server {
        listen 127.0.0.1:8082;

        proxy_ssl on;
        proxy_ssl_verify off;

        proxy_pass x.x.x.x:443;
    }
}
Enter fullscreen mode Exit fullscreen mode

streamはTCPプロキシなので、

HTTPヘッダーを解析しません。

そのためATEN IPMIとの相性問題を回避できます。

Nginx HTTP Proxyはおすすめしない

一般的には

proxy_pass https://x.x.x.x;
Enter fullscreen mode Exit fullscreen mode

というHTTPリバースプロキシを書きたくなります。

しかしATEN IPMIでは、

upstream sent invalid header
Enter fullscreen mode Exit fullscreen mode

になるケースがあります。

これは

  • Hostヘッダー
  • Origin
  • WebSocket

以前に、

HTTPレスポンス自体がNginxに拒否されるためです。

バッファサイズや proxy_buffering off などでは改善しません。

なぜsocatが動くのか

socatはHTTPを理解していません。

単なるTCP中継です。

そのため、

ATEN IPMIが多少変わったHTTPレスポンスを返しても、

そのままブラウザへ転送します。

結果として、

ブラウザは問題なく処理できます。

結論

ATENベースのSupermicro X11 IPMIをTailscale Servicesで公開する場合のおすすめ順位は次の通りです。

  1. socat + tailscale serve --tls-terminated-tcp
  2. Nginx stream + tailscale serve --tls-terminated-tcp
  3. Nginx HTTP Proxy(非推奨)

一般的なWebアプリではHTTPリバースプロキシが第一選択ですが、ATEN IPMIでは事情が異なります。

HTTPを解析するプロキシよりも、TCPレベルでそのまま中継する構成の方が安定して動作します。

もし upstream sent invalid header502 Bad Gateway に遭遇した場合は、WebSocketやTailscaleを疑う前に、HTTPプロキシを経由していないか確認してみてください。

Top comments (0)