diff --git a/README.md b/README.md index 7a00c3a6a9..3f664317e7 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,11 @@ Golang SQL database driver for [Yandex ClickHouse](https://clickhouse.yandex/) * block_size - maximum rows in block (default is 1000000). If the rows are larger then the data will be split into several blocks to send them to the server * debug - enable debug output (boolean value) +SSL/TLS parameters: + +* secure - establish secure connection (default is false) +* skip_verify - skip certificate verification (default is true) + example: ``` tcp://host1:9000?username=user&password=qwerty&database=clicks&read_timeout=10&write_timeout=20&alt_hosts=host2:9000,host3:9000 diff --git a/bootstrap.go b/bootstrap.go index ef58fda5f1..86e1493a17 100644 --- a/bootstrap.go +++ b/bootstrap.go @@ -48,6 +48,8 @@ func open(dsn string) (*clickhouse, error) { var ( hosts = []string{url.Host} query = url.Query() + secure = false + skipVerify = true noDelay = true compress = false database = query.Get("database") @@ -67,6 +69,12 @@ func open(dsn string) (*clickhouse, error) { if v, err := strconv.ParseBool(query.Get("no_delay")); err == nil && !v { noDelay = false } + if v, err := strconv.ParseBool(query.Get("secure")); err == nil && v { + secure = true + } + if v, err := strconv.ParseBool(query.Get("skip_verify")); err == nil && !v { + skipVerify = false + } if duration, err := strconv.ParseFloat(query.Get("read_timeout"), 64); err == nil { readTimeout = time.Duration(duration * float64(time.Second)) } @@ -115,7 +123,7 @@ func open(dsn string) (*clickhouse, error) { database, username, ) - if ch.conn, err = dial("tcp", hosts, noDelay, connOpenStrategy, ch.logf); err != nil { + if ch.conn, err = dial(secure, skipVerify, hosts, noDelay, connOpenStrategy, ch.logf); err != nil { return nil, err } logger.SetPrefix(fmt.Sprintf("[clickhouse][connect=%d]", ch.conn.ident)) diff --git a/connect.go b/connect.go index 1edaa04f49..b532184edf 100644 --- a/connect.go +++ b/connect.go @@ -2,6 +2,7 @@ package clickhouse import ( "bufio" + "crypto/tls" "database/sql/driver" "net" "sync/atomic" @@ -25,7 +26,7 @@ const ( connOpenInOrder ) -func dial(network string, hosts []string, noDelay bool, openStrategy openStrategy, logf func(string, ...interface{})) (*connect, error) { +func dial(secure, skipVerify bool, hosts []string, noDelay bool, openStrategy openStrategy, logf func(string, ...interface{})) (*connect, error) { var ( err error abs = func(v int) int { @@ -45,8 +46,22 @@ func dial(network string, hosts []string, noDelay bool, openStrategy openStrateg case connOpenRandom: num = (ident + 1) % len(hosts) } - if conn, err = net.DialTimeout(network, hosts[num], 20*time.Second); err == nil { - logf("[dial] strategy=%s, ident=%d, server=%d -> %s", openStrategy, ident, num, conn.RemoteAddr()) + switch { + case secure: + conn, err = tls.DialWithDialer( + &net.Dialer{ + Timeout: 5 * time.Second, + }, + "tcp", + hosts[num], + &tls.Config{ + InsecureSkipVerify: skipVerify, + }) + default: + conn, err = net.DialTimeout("tcp", hosts[num], 5*time.Second) + } + if err == nil { + logf("[dial] secure=%t, skip_verify=%t, strategy=%s, ident=%d, server=%d -> %s", secure, skipVerify, openStrategy, ident, num, conn.RemoteAddr()) if tcp, ok := conn.(*net.TCPConn); ok { tcp.SetNoDelay(noDelay) // Disable or enable the Nagle Algorithm for this tcp socket }