OpenSearch 的 demo security 設定預設啟用,9200 REST 層只走 TLS。用
http://訪問時服務端 TLS 握手失敗、直接關閉連接,curl 收不到任何響應字節,報curl: (52) Empty reply from server。改用https://並帶上-k和憑據即可。適用 Docker 鏡像預設設定下的 OpenSearch 2.12+ 全版本。
Issue & Env(案發現場與環境)
- 業務場景:用官方 Docker 鏡像起單節點 OpenSearch,按 Elasticsearch 習慣先
curl http://localhost:9200探活——連接秒建、不報錯,但響應是空的。 - 環境:OpenSearch 3.9.0 stable,鏡像
opensearchproject/opensearch:latest,demo security 設定預設啟用(TLS + 自簽證書),macOS / Linux / Docker 均可復現。 - 關鍵區分:不是超時,也不是拒絕連接。TCP 連上了,服務端主動斷開,一個字節都沒回;補
-u憑據也沒用,因為請求根本沒進 HTTP 層。
StackTrace(堆疊日誌)
客戶端側只有一行,信息量很少:
curl http://localhost:9200
# curl: (52) Empty reply from server
真正的成因在服務端日誌裡。OpenSearch 官方論壇帖 #20152(OpenSearch 2.15,Helm 部署)記錄了完整現場:
[opensearch@opensearch-cluster-master-0 ~]$ curl http://opensearch-cluster-master:9200 --insecure -u "admin:Moto1sBe\$t"
curl: (52) Empty reply from server
[2024-07-05T09:05:30,790][ERROR][o.o.h.n.s.SecureNetty4HttpServerTransport] [opensearch-cluster-master-0] Exception during establishing a SSL connection: io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record: 474554202f20485454502f312e310d0a486f73743a206c6f63616c686f73743a393230300d0a557365722d4167656e743a206375726c2f382e352e300d0a4163636570743a202a2f2a0d0a0d0a
那串十六進制不是隨機字節,解碼出來恰好是 curl 發出的明文 HTTP 請求:
echo "474554202f20485454502f312e310d0a" | xxd -r -p
# GET / HTTP/1.1
Reproduce(復現程式碼)
最小復現——官方鏡像預設設定啟動單節點:
docker run -d -p 9200:9200 -p 9600:9600 \
-e "discovery.type=single-node" \
-e "OPENSEARCH_INITIAL_ADMIN_PASSWORD=Devfix@2026" \
opensearchproject/opensearch:latest
2.12+ 必須傳 OPENSEARCH_INITIAL_ADMIN_PASSWORD,至少 8 位且大小寫、數字、特殊字符各一,否則容器直接退出。等啟動完成後分別探測:
# 明文 http:连接建立,空响应断开
curl http://localhost:9200
# curl: (52) Empty reply from server
# 补上凭据,结果不变——问题不在认证层
curl -u admin:Devfix@2026 http://localhost:9200
# curl: (52) Empty reply from server
# 协议换成 https,立刻正常
curl -sk -u admin:Devfix@2026 "https://localhost:9200"
Root Cause(根因分析)
TLS 端口收到明文請求
demo security 設定把 plugins.security.ssl.http.enabled 置為 true,官方 security configuration 文檔裡就是這個鍵。9200 端口還是那個端口,但 Netty 的 SslHandler 擋在前面:連接建立後服務端期待的第一段數據是 TLS 握手記錄,不是 HTTP 報文。
curl 用 http:// 發出明文 GET / HTTP/1.1,服務端按 TLS 記錄格式解析失敗,拋 NotSslRecordException 後直接關閉連接。日誌裡那串 hex 正是被拒收的明文請求本身。
(52) 的語義
curl 錯誤碼 52 對應 CURLE_GOT_NOTHING:TCP 連接成功建立,但服務端在關閉前沒發出任何響應字節。和幾個相近錯誤區分開:
curl: (7) Failed to connect:端口沒監聽或被防火牆攔截,連接層就失敗;curl: (28) Operation timed out:包被黑洞,等不到響應;curl: (52) Empty reply from server:連上了、握手失敗、空著斷開。
認證不是本案的鍋
看到 (52) 很容易聯想到“沒傳憑據”。缺憑據的場景是 TLS 成功後返回 401 JSON,有完整響應體。本案請求連 HTTP 協議棧都沒進去,-u 和 --insecure 都救不了,解法只能在協議層:換 https。
The Fix(解決方案)
方案一:改用 https(推薦)
curl -sk -u admin:Devfix@2026 "https://localhost:9200"
-k 跳過自簽證書校驗,僅適合本地體驗。生產環境按官方文檔替換 demo 證書,用 --cacert root-ca.pem 指定 CA,不再加 -k。
方案二:禁用 security 插件(僅限本地體驗)
docker run -d -p 9200:9200 \
-e "discovery.type=single-node" \
-e "DISABLE_SECURITY_PLUGIN=true" \
opensearchproject/opensearch:latest
curl http://localhost:9200
禁用後 9200 回到明文 HTTP。代價是沒有認證和授權,所有索引公開讀寫,不要暴露在任何不受控網絡上。
驗證修復
curl -sk -o /dev/null -w "%{http_code}\n" -u admin:Devfix@2026 "https://localhost:9200"
# 期望输出:200
客戶端 SDK 同理
opensearch-py、Java 客戶端等預設仍按明文 http 連接,啟用 security 後要在設定裡顯式打開 https、傳 basic auth 或 CA 證書,否則報錯形態相同——空響應或連接被重置。
注意事項
- 本地部署的完整流程和其餘常見卡點,見 從零跑通 OpenSearch。
- OpenSearch Dashboards 連的是同一個 9200,
OPENSEARCH_HOSTS也要寫https://並處理證書,否則 Dashboards 同樣連不上。
References(參考來源)
- Cannot disable REST Layer SSL — OpenSearch 官方論壇opensearch-forum
- Disable Rest Layer SSL with Security Plugin Enabled — OpenSearch 官方論壇opensearch-forum
- Security configuration — OpenSearch 官方文檔opensearch-docs
- Docker — OpenSearch 官方文檔opensearch-docs
- Disabling and enabling the Security plugin — OpenSearch 官方文檔opensearch-docs
