Files
xinmi/internal/application/repository/retriever/opensearch/transport.go
T
yawan f7fc992f57
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
2026-09-22 16:19:06 +08:00

81 lines
3.1 KiB
Go

package opensearch
import (
"crypto/tls"
"fmt"
"net/http"
"time"
"github.com/opensearch-project/opensearch-go/v4"
osapi "github.com/opensearch-project/opensearch-go/v4/opensearchapi"
"github.com/Tencent/WeKnora/internal/types"
secutils "github.com/Tencent/WeKnora/internal/utils"
)
// NewOpenSearchClient builds a TLS-hardened, pool-tuned *osapi.Client for
// the OpenSearch driver. The caller wires it into the registry from the
// env path (container) and the DB-store path (engine factory); the
// Repository constructor itself receives the pre-built client.
//
// TLS posture:
// - MinVersion: TLS 1.2 (TLS 1.3 negotiated when both ends support).
// - InsecureSkipVerify: opt-in via ConnectionConfig.InsecureSkipVerify.
// Default false. Operators set true ONLY for self-signed dev clusters.
// - CipherSuites: explicit forward-secrecy-only list. TLS 1.2's default
// cipher list includes TLS_RSA_* (no forward secrecy); we drop them.
// TLS 1.3 cipher selection is automatic and safe.
//
// Transport tuning:
// - MaxIdleConnsPerHost: 32 (Go default 2 is too low when callers
// dispatch concurrent queries across multiple knowledge bases)
// - IdleConnTimeout: 90s (typical LB keep-alive)
// - ResponseHeaderTimeout: 30s (per-request safety net)
// - ExpectContinueTimeout: 1s
func NewOpenSearchClient(cfg *types.ConnectionConfig) (*osapi.Client, error) {
if cfg == nil || cfg.Addr == "" {
return nil, fmt.Errorf("opensearch: ConnectionConfig.Addr required: %w", ErrConfigInvalid)
}
if err := secutils.ValidateURLForSSRF(cfg.Addr); err != nil {
return nil, fmt.Errorf("opensearch: address failed SSRF validation: %w", err)
}
transport := buildHTTPTransport(cfg.InsecureSkipVerify)
return osapi.NewClient(osapi.Config{
Client: opensearch.Config{
Addresses: []string{cfg.Addr},
Username: cfg.Username,
Password: cfg.Password,
Transport: &secutils.SSRFValidatingRoundTripper{Base: transport},
},
})
}
// buildHTTPTransport constructs the TLS-hardened, pool-tuned
// *http.Transport used by every OpenSearch client this driver creates.
// Extracted from newOpenSearchClient so unit tests can pin the TLS
// posture directly (the opensearch-go SDK wraps the transport in an
// internal interface, making reflection-based introspection of the
// final client fragile across SDK versions).
func buildHTTPTransport(insecureSkipVerify bool) *http.Transport {
return &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: secutils.SSRFSafeDialContext,
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: insecureSkipVerify, //nolint:gosec — operator opt-in flag
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
},
},
MaxIdleConnsPerHost: 32,
IdleConnTimeout: 90 * time.Second,
ResponseHeaderTimeout: 30 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
}