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
socator Nginxstream) 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/
With Tailscale Services, this can be configured as:
tailscale serve \
--service=svc:x11dpi-ipmi \
--https=443 \
https+insecure://x.x.x.x
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
Upgradeheader 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..."
resulting in:
502 Bad Gateway
The request flow looked like this:
Browser
↓
Tailscale
↓
Nginx HTTP Proxy
↓
ATEN IPMI
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
fail.
For example:
op=UID_SUPPORT.XML
returns 502 Bad Gateway when sent via POST.
Meanwhile:
GET /cgi/ipmi.cgi
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
For example:
socat \
TCP4-LISTEN:8082,bind=127.0.0.1,reuseaddr,fork \
OPENSSL:x.x.x.x:443,verify=0
Then configure Tailscale Serve:
tailscale serve \
--service=svc:x11dpi-ipmi \
--tls-terminated-tcp=443 \
tcp://127.0.0.1:8082
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;
}
}
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;
However, with ATEN IPMI this can result in:
upstream sent invalid header
This happens before considerations such as:
-
Hostheaders 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:
socat+tailscale serve --tls-terminated-tcp- Nginx
stream+tailscale serve --tls-terminated-tcp - 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/
のようなサービス名で公開したい。
Tailscale Services を使えば、
tailscale serve \
--service=svc:x11dpi-ipmi \
--https=443 \
https+insecure://x.x.x.x
のような構成が作れます。
しかし、実際にはHTML5 KVMが正常に動作しませんでした。
最初に疑ったのはWebSocket
HTML5 KVMはWebSocketを使用しています。
そのため、
- Tailscale ServeがWebSocketに対応していないのでは?
- Upgradeヘッダーが落ちているのでは?
と考えました。
しかし、自作の aiohttp WebSocketサーバーでは正常に動作しました。
つまり、
- Tailscale
- WebSocket
- ブラウザ
の組み合わせ自体には問題がありません。
実際の原因
NginxをHTTPリバースプロキシとして挟いて調査すると、
upstream sent invalid header: "\x20..."
というエラーが出ました。
つまり、
502 Bad Gateway
になっています。
これは
ブラウザ
↓
Tailscale
↓
Nginx HTTP Proxy
↓
ATEN IPMI
という構成で、
ATEN IPMIが返すHTTPレスポンスヘッダーをNginxが不正と判断して拒否している
ことを意味します。
GETでは問題なくても、
POST /cgi/ipmi.cgi
のようなCGIでは失敗します。
例えば、
op=UID_SUPPORT.XML
をPOSTすると502になります。
一方、
GET /cgi/ipmi.cgi
は正常に返ります。
つまり、
HTTPレベルの互換性問題です。
これはWebSocketの問題ではない
最初はHTML5 KVMだけが失敗するのでWebSocketを疑いました。
しかし実際には、
CGIのPOSTレスポンスの時点でHTTPパーサが失敗していました。
そのため、
HTML5 KVM以前にIPMIとのHTTP通信自体が成立していません。
解決策1: socat(おすすめ)
HTTPを一切解釈せず、
Tailscale
↓ TLS終端
TCP
↓
socat
↓ TLS
IPMI
という構成にします。
例えば
socat \
TCP4-LISTEN:8082,bind=127.0.0.1,reuseaddr,fork \
OPENSSL:x.x.x.x:443,verify=0
そして
tailscale serve \
--service=svc:x11dpi-ipmi \
--tls-terminated-tcp=443 \
tcp://127.0.0.1:8082
とします。
この構成では、
- 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;
}
}
streamはTCPプロキシなので、
HTTPヘッダーを解析しません。
そのためATEN IPMIとの相性問題を回避できます。
Nginx HTTP Proxyはおすすめしない
一般的には
proxy_pass https://x.x.x.x;
というHTTPリバースプロキシを書きたくなります。
しかしATEN IPMIでは、
upstream sent invalid header
になるケースがあります。
これは
- Hostヘッダー
- Origin
- WebSocket
以前に、
HTTPレスポンス自体がNginxに拒否されるためです。
バッファサイズや proxy_buffering off などでは改善しません。
なぜsocatが動くのか
socatはHTTPを理解していません。
単なるTCP中継です。
そのため、
ATEN IPMIが多少変わったHTTPレスポンスを返しても、
そのままブラウザへ転送します。
結果として、
ブラウザは問題なく処理できます。
結論
ATENベースのSupermicro X11 IPMIをTailscale Servicesで公開する場合のおすすめ順位は次の通りです。
- socat +
tailscale serve --tls-terminated-tcp - Nginx stream +
tailscale serve --tls-terminated-tcp - Nginx HTTP Proxy(非推奨)
一般的なWebアプリではHTTPリバースプロキシが第一選択ですが、ATEN IPMIでは事情が異なります。
HTTPを解析するプロキシよりも、TCPレベルでそのまま中継する構成の方が安定して動作します。
もし upstream sent invalid header や 502 Bad Gateway に遭遇した場合は、WebSocketやTailscaleを疑う前に、HTTPプロキシを経由していないか確認してみてください。
Top comments (0)