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
