{"record":{"id":"2239e74abd7f8c8a","repo":"Tencent/WeKnora","slug":"sandbox-remote-docker-host-q-requires-a-tls-cert","errorCode":null,"errorMessage":"sandbox: remote docker host %q requires a TLS certificate directory","messagePattern":"sandbox: remote docker host %q requires a TLS certificate directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sandbox/docker_engine.go","lineNumber":302,"sourceCode":"}\n\n// ValidateDockerRemoteTLS requires client certificates for a TCP daemon.\n// A remote Engine API that accepts container creation is a root shell on\n// that host; plaintext tcp://2375 is not an acceptable way to reach it.\n// Unix sockets are local to the WeKnora process and do not use TLS.\nfunc ValidateDockerRemoteTLS(host, tlsCertPath string) error {\n\ttrimmed := strings.TrimSpace(host)\n\tif trimmed == \"\" {\n\t\treturn nil\n\t}\n\tscheme, _, found := strings.Cut(trimmed, \"://\")\n\tif !found {\n\t\treturn nil\n\t}\n\tswitch strings.ToLower(scheme) {\n\tcase \"tcp\", \"http\", \"https\":\n\t\tif strings.TrimSpace(tlsCertPath) == \"\" {\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"sandbox: remote docker host %q requires a TLS certificate directory\", host)\n\t\t}\n\t}\n\treturn nil\n}\n\n// ValidateDockerNetworkMode allows only bridge (egress) and none (no egress).\n//\n// host and container: modes share another namespace outright, which would put\n// sandbox code on the WeKnora host's or a sibling container's network. A\n// user-defined network name is refused for the weaker but equally real version\n// of the same problem: the usual deployment reaches its daemon through the\n// mounted docker.sock, so naming the deployment's own compose network would\n// place a sandbox on the same L3 network as Postgres and Redis. Only the\n// operator can judge what a given named network exposes, and this value is set\n// per workspace config, so it is not theirs to choose.\nfunc ValidateDockerNetworkMode(mode string) error {\n\ttrimmed := strings.TrimSpace(mode)","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/sandbox/docker_engine.go#L284-L320","documentation":"ValidateDockerRemoteTLS enforces that remote docker daemons (tcp/http/https schemes) are reached with TLS. Because a remote Engine API that can create containers is effectively root on that host, a TLS certificate directory (client certs/CA) must be configured; an empty tlsCertPath yields this error.","triggerScenarios":"Calling ResolveEffectiveConfig (or dockerSettingsFromConfig) with a docker host whose scheme is tcp://, http://, or https:// while the TLS certificate directory field is empty or whitespace-only.","commonSituations":"Pointing at a remote daemon (tcp://build-host:2376) without setting up the client cert directory; migrating a local unix:// config to a remote host and forgetting the TLS settings; daemons started with plaintext 2375 ports.","solutions":["Set the TLS certificate directory config field to the path containing ca.pem, cert.pem, and key.pem generated for the daemon","Regenerate client certificates with the daemon's CA if the directory is empty","If the daemon is genuinely local, switch the host to unix:///var/run/docker.sock so the TLS requirement does not apply","Never fall back to plaintext tcp://: the library intentionally requires TLS for remote Engine APIs"],"exampleFix":"// before\nDocker: {Host: \"tcp://build-host:2376\"} // no TLS\n// after\nDocker: {Host: \"tcp://build-host:2376\", TLSCertPath: \"/etc/docker/client-certs\"} // contains ca/cert/key.pem","handlingStrategy":"validation","validationCode":"if isRemoteHost(cfg.Docker.Host) && strings.TrimSpace(cfg.Docker.TLSCertPath) == \"\" {\n    return fmt.Errorf(\"remote docker host %s needs TLSCertPath with ca.pem/cert.pem/key.pem\", cfg.Docker.Host)\n}\nif err := sandbox.ValidateDockerRemoteTLS(cfg.Docker.Host, cfg.Docker.TLSCertPath); err != nil { return err }","typeGuard":"func remoteHostHasTLS(host, tlsCertPath string) bool {\n    scheme, _, found := strings.Cut(strings.TrimSpace(host), \"://\")\n    if !found { return true }\n    switch strings.ToLower(scheme) { case \"tcp\", \"http\", \"https\": return strings.TrimSpace(tlsCertPath) != \"\" }\n    return true\n}","tryCatchPattern":"if err := sandbox.ValidateDockerRemoteTLS(cfg.Docker.Host, cfg.Docker.TLSCertPath); err != nil {\n    if strings.Contains(err.Error(), \"requires a TLS certificate directory\") { /* prompt operator for cert dir */ }\n    return err\n}","preventionTips":["Provision ca.pem/cert.pem/key.pem on every host that talks to a remote daemon","Prefer unix:// for local daemons to sidestep the TLS requirement entirely","Never configure plaintext tcp://:2375 — the policy intentionally blocks it","Validate TLS settings in CI config tests via ResolveEffectiveConfig"],"tags":["sandbox","docker","tls","security","configuration"],"backgroundTag":"missing-tls-certificate","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}