go/cmd/dolt: Allow the Dolt CLI to connect to a running dolt sql-server which is configured with require_secure_transport: true.

When Dolt CLI is running in a directory with a corresponding running sql-server
process, it will connect to the server process and complete its work using SQL
statements. Previously, the Dolt CLI was configured to always connect on a
plaintext TCP connection for these connections. That meant it did not work for
servers configured with require_secure_transport: true. One consequence was
that the published Dolt dockerhub image did not work with
require_secure_transport: true, since that image runs `dolt sql` against the
running server as part of its entrypoint.

This changes `dolt` CLI to connect over (non-verified) TLS if such as an option
is presented by the server. The CLI still falls back to plaintext as well.

Dolt CLI still does not work when the server is configured with
require_client_certificate, since Dolt CLI does not currently have a way to
configure its presented client certificate and private key. As a consequence,
at least for the time being, the published DockerHub images for Dolt do not
work with require_client_certificate: true.
This commit is contained in:
Aaron Son
2025-11-24 13:39:08 -08:00
parent ea146065a0
commit a2fe5295b3
3 changed files with 53 additions and 7 deletions

View File

@@ -16,6 +16,7 @@ package sqlserver
import (
"context"
"crypto/tls"
sql2 "database/sql"
"fmt"
"io"
@@ -36,9 +37,33 @@ import (
"github.com/dolthub/dolt/go/libraries/utils/filesys"
)
type QueryistTLSMode int
const (
QueryistTLSMode_Disabled QueryistTLSMode = iota
// Require TLS, verify the server certificate using the system
// trust store, do not allow fallback to plaintext.
//
// Used for `dolt --host ... sql ...` when `--no-tls-` is not
// specified. Often used for connecting to Hosted DoltDB
// instances using the CLI commands posted on
// hosted.doltdb.com.
QueryistTLSMode_Enabled
// Used for local Dolt CLI queryist connecting to the running
// local server. In this mode, TLS is allowed but not required
// and the client does not verify the remote TLS
// certificate. It is assumed connecting to the port locally
// is secure and lands the client in the correct place, given
// the contents of sql-server.info, for example.
//
// This mode still does not allow the Dolt CLI to connect to a
// server which requires a client certificate.
QueryistTLSMode_NoVerify_FallbackToPlaintext
)
// BuildConnectionStringQueryist returns a Queryist that connects to the server specified by the given server config. Presence in this
// module isn't ideal, but it's the only way to get the server config into the queryist.
func BuildConnectionStringQueryist(ctx context.Context, cwdFS filesys.Filesys, creds *cli.UserPassword, apr *argparser.ArgParseResults, host string, port int, useTLS bool, dbRev string) (cli.LateBindQueryist, error) {
func BuildConnectionStringQueryist(ctx context.Context, cwdFS filesys.Filesys, creds *cli.UserPassword, apr *argparser.ArgParseResults, host string, port int, tlsMode QueryistTLSMode, dbRev string) (cli.LateBindQueryist, error) {
clientConfig, err := GetClientConfig(cwdFS, creds, apr)
if err != nil {
return nil, err
@@ -54,8 +79,13 @@ func BuildConnectionStringQueryist(ctx context.Context, cwdFS filesys.Filesys, c
parsedMySQLConfig.DBName = dbRev
parsedMySQLConfig.Addr = fmt.Sprintf("%s:%d", host, port)
if useTLS {
parsedMySQLConfig.TLSConfig = "true"
switch tlsMode {
case QueryistTLSMode_Disabled:
case QueryistTLSMode_Enabled:
parsedMySQLConfig.TLS = &tls.Config{}
case QueryistTLSMode_NoVerify_FallbackToPlaintext:
parsedMySQLConfig.TLS = &tls.Config{InsecureSkipVerify: true}
parsedMySQLConfig.AllowFallbackToPlaintext = true
}
mysqlConnector, err := mysql.NewConnector(parsedMySQLConfig)

View File

@@ -623,8 +623,11 @@ If you're interested in running this command against a remote host, hit us up on
if !hasPort {
port = 3306
}
useTLS := !apr.Contains(cli.NoTLSFlag)
return sqlserver.BuildConnectionStringQueryist(ctx, cwdFS, creds, apr, host, port, useTLS, useDb)
tlsMode := sqlserver.QueryistTLSMode_Enabled
if apr.Contains(cli.NoTLSFlag) {
tlsMode = sqlserver.QueryistTLSMode_Disabled
}
return sqlserver.BuildConnectionStringQueryist(ctx, cwdFS, creds, apr, host, port, tlsMode, useDb)
} else {
_, hasPort := apr.GetInt(cli.PortFlag)
if hasPort {
@@ -712,7 +715,7 @@ If you're interested in running this command against a remote host, hit us up on
if !creds.Specified {
creds = &cli.UserPassword{Username: sqlserver.LocalConnectionUser, Password: localCreds.Secret, Specified: false}
}
return sqlserver.BuildConnectionStringQueryist(ctx, cwdFS, creds, apr, "localhost", localCreds.Port, false, useDb)
return sqlserver.BuildConnectionStringQueryist(ctx, cwdFS, creds, apr, "localhost", localCreds.Port, sqlserver.QueryistTLSMode_NoVerify_FallbackToPlaintext, useDb)
}
}

View File

@@ -3,6 +3,7 @@ load $BATS_TEST_DIRNAME/helper/common.bash
load $BATS_TEST_DIRNAME/helper/query-server-common.bash
REQUIRE_CLIENT_CERT=false
REQUIRE_SECURE_TRANSPORT=false
setup() {
skiponwindows "tests are flaky on Windows"
@@ -27,6 +28,7 @@ listener:
host: "0.0.0.0"
port: $PORT
require_client_cert: $REQUIRE_CLIENT_CERT
require_secure_transport: $REQUIRE_SECURE_TRANSPORT
tls_cert: $CERTS_DIR/server-cert.pem
tls_key: $CERTS_DIR/server-key.pem
EOF
@@ -48,6 +50,7 @@ listener:
host: "0.0.0.0"
port: $PORT
require_client_cert: $REQUIRE_CLIENT_CERT
require_secure_transport: $REQUIRE_SECURE_TRANSPORT
ca_cert: $CERTS_DIR/ca.pem
tls_cert: $CERTS_DIR/server-cert.pem
tls_key: $CERTS_DIR/server-key.pem
@@ -381,6 +384,16 @@ EOF
[[ "$output" =~ "123" ]] || false
}
# bats test_tags=no_lambda
@test "mutual-tls-auth: dolt cli works with require_secure_transport" {
REQUIRE_SECURE_TRANSPORT=true
start_sql_server_with_TLS
run dolt sql -q 'show databases'
[ "$status" -eq 0 ] || false
[[ "$output" =~ "Database" ]] || false
}
# bats test_tags=no_lambda
@test "mutual-tls-auth: auth works with require_client_cert (without cert verification)" {
dolt sql -q "create user user1@'%';"
@@ -447,7 +460,7 @@ EOF
run dolt sql -q "SELECT 1;"
[ "$status" -ne 0 ]
[[ "$output" =~ "UNAVAILABLE" ]] || false
[[ "$output" =~ "remote error: tls: certificate required" ]] || false
}
# bats test_tags=no_lambda