Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add tlsmin connection parameter #45

Merged
merged 5 commits into from
Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ Other supported formats are listed below.
* 16 log statement parameters
* 32 log transaction begin/end
* `TrustServerCertificate`
* false - Server certificate is checked. Default is false if encypt is specified.
* false - Server certificate is checked. Default is false if encrypt is specified.
* true - Server certificate is not checked. Default is true if encrypt is not specified. If trust server certificate is true, driver accepts any certificate presented by the server and any host name in that certificate. In this mode, TLS is susceptible to man-in-the-middle attacks. This should be used only for testing.
* `certificate` - The file that contains the public key certificate of the CA that signed the SQL Server certificate. The specified certificate overrides the go platform specific CA certificates.
* `hostNameInCertificate` - Specifies the Common Name (CN) in the server certificate. Default value is the server host.
* `tlsmin` - Specifies the minimum TLS version for negotiating encryption with the server. Recognized values are `1.0`, `1.1`, `1.2`, `1.3`. If not set to a recognized value the default value for the `tls` package will be used. The default is currently `1.2`.
* `ServerSPN` - The kerberos SPN (Service Principal Name) for the server. Default is MSSQLSvc/host:port.
* `Workstation ID` - The workstation name (default is the host name)
* `ApplicationIntent` - Can be given the value `ReadOnly` to initiate a read-only connection to an Availability Group listener. The `database` must be specified when connecting with `Application Intent` set to `ReadOnly`.
Expand Down
17 changes: 15 additions & 2 deletions msdsn/conn_str.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ type Config struct {
PacketSize uint16
}

func SetupTLS(certificate string, insecureSkipVerify bool, hostInCertificate string) (*tls.Config, error) {
func SetupTLS(certificate string, insecureSkipVerify bool, hostInCertificate string, minTlsVersion string) (*tls.Config, error) {
config := tls.Config{
ServerName: hostInCertificate,
InsecureSkipVerify: insecureSkipVerify,
Expand All @@ -86,6 +86,18 @@ func SetupTLS(certificate string, insecureSkipVerify bool, hostInCertificate str
// Setting DynamicRecordSizingDisabled to true disables that algorithm and uses 16384 bytes per TLS package
DynamicRecordSizingDisabled: true,
}
switch minTlsVersion {
case "1.0":
config.MinVersion = tls.VersionTLS10
case "1.1":
config.MinVersion = tls.VersionTLS11
case "1.2":
config.MinVersion = tls.VersionTLS12
case "1.3":
config.MinVersion = tls.VersionTLS13
default:
// use the tls package default
}
if len(certificate) == 0 {
return &config, nil
}
Expand Down Expand Up @@ -256,8 +268,9 @@ func Parse(dsn string) (Config, map[string]string, error) {
}

if p.Encryption != EncryptionDisabled {
tlsMin := params["tlsmin"]
var err error
p.TLSConfig, err = SetupTLS(certificate, trustServerCert, hostInCertificate)
p.TLSConfig, err = SetupTLS(certificate, trustServerCert, hostInCertificate, tlsMin)
if err != nil {
return p, params, fmt.Errorf("failed to setup TLS: %w", err)
}
Expand Down
25 changes: 24 additions & 1 deletion msdsn/conn_str_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package msdsn

import (
"crypto/tls"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -59,7 +60,26 @@ func TestValidConnectionString(t *testing.T) {
{"failoverpartner=fopartner;failoverport=2000", func(p Config) bool { return p.FailOverPartner == "fopartner" && p.FailOverPort == 2000 }},
{"app name=appname;applicationintent=ReadOnly;database=testdb", func(p Config) bool { return p.AppName == "appname" && p.ReadOnlyIntent }},
{"encrypt=disable", func(p Config) bool { return p.Encryption == EncryptionDisabled }},
{"encrypt=true", func(p Config) bool { return p.Encryption == EncryptionRequired }},
{"encrypt=disable;tlsmin=1.1", func(p Config) bool { return p.Encryption == EncryptionDisabled && p.TLSConfig == nil }},
{"encrypt=true", func(p Config) bool { return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == 0 }},
{"encrypt=true;tlsmin=1.0", func(p Config) bool {
return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == tls.VersionTLS10
}},
{"encrypt=false;tlsmin=1.0", func(p Config) bool {
return p.Encryption == EncryptionOff && p.TLSConfig.MinVersion == tls.VersionTLS10
}},
{"encrypt=true;tlsmin=1.1", func(p Config) bool {
return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == tls.VersionTLS11
}},
{"encrypt=true;tlsmin=1.2", func(p Config) bool {
return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == tls.VersionTLS12
}},
{"encrypt=true;tlsmin=1.3", func(p Config) bool {
return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == tls.VersionTLS13
}},
{"encrypt=true;tlsmin=1.4", func(p Config) bool {
return p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == 0
}},
{"encrypt=false", func(p Config) bool { return p.Encryption == EncryptionOff }},
{"connection timeout=3;dial timeout=4;keepalive=5", func(p Config) bool {
return p.ConnTimeout == 3*time.Second && p.DialTimeout == 4*time.Second && p.KeepAlive == 5*time.Second
Expand Down Expand Up @@ -159,6 +179,9 @@ func TestValidConnectionString(t *testing.T) {
{"sqlserver://someuser@somehost?connection+timeout=30&disableretry=1", func(p Config) bool {
return p.Host == "somehost" && p.Port == 0 && p.Instance == "" && p.User == "someuser" && p.Password == "" && p.ConnTimeout == 30*time.Second && p.DisableRetry
}},
{"sqlserver://somehost?encrypt=true&tlsmin=1.1", func(p Config) bool {
return p.Host == "somehost" && p.Encryption == EncryptionRequired && p.TLSConfig.MinVersion == tls.VersionTLS11
}},
}
for _, ts := range connStrings {
p, _, err := Parse(ts.connStr)
Expand Down
2 changes: 1 addition & 1 deletion tds.go
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,7 @@ initiate_connection:
}
}
if config == nil {
config, err = msdsn.SetupTLS("", false, p.Host)
config, err = msdsn.SetupTLS("", false, p.Host, "")
if err != nil {
return nil, err
}
Expand Down