diff --git a/connection.go b/connection.go index f0ea1a833..ad8c0eee5 100644 --- a/connection.go +++ b/connection.go @@ -787,7 +787,7 @@ func buildSnowflakeConn(ctx context.Context, config Config) (*snowflakeConn, err } var st http.RoundTripper = SnowflakeTransport if sc.cfg.Transporter == nil { - if sc.cfg.InsecureMode { + if sc.cfg.DisableOCSPChecks || sc.cfg.InsecureMode { // no revocation check with OCSP. Think twice when you want to enable this option. st = snowflakeInsecureTransport } else { diff --git a/connection_configuration.go b/connection_configuration.go index d2e191aef..e7633ddd3 100644 --- a/connection_configuration.go +++ b/connection_configuration.go @@ -134,6 +134,8 @@ func handleSingleParam(cfg *Config, key string, value interface{}) error { } err = determineAuthenticatorType(cfg, v) case "insecuremode": + // TODO + logInsecureModeDeprecationInfo() cfg.InsecureMode, err = parseBool(value) case "ocspfailopen": var vv ConfigBool diff --git a/doc.go b/doc.go index 82d8a1b05..4c6a72a6f 100644 --- a/doc.go +++ b/doc.go @@ -106,7 +106,7 @@ The following connection parameters are supported: - application: Identifies your application to Snowflake Support. - - insecureMode: false by default. Set to true to bypass the Online + - disableOCSPChecks: false by default. Set to true to bypass the Online Certificate Status Protocol (OCSP) certificate revocation check. IMPORTANT: Change the default value for testing or emergency situations only. diff --git a/driver_test.go b/driver_test.go index f7ee5f0b9..86364054e 100644 --- a/driver_test.go +++ b/driver_test.go @@ -1793,7 +1793,7 @@ func TestOpenWithTransport(t *testing.T) { // Test that transport override also works in insecure mode countingTransport.requests = 0 - config.InsecureMode = true + config.DisableOCSPChecks = true db, err = driver.OpenWithConfig(context.Background(), *config) if err != nil { t.Fatalf("failed to open with config. config: %v, err: %v", config, err) diff --git a/dsn.go b/dsn.go index 9600af381..24b48be51 100644 --- a/dsn.go +++ b/dsn.go @@ -13,6 +13,7 @@ import ( "net/http" "net/url" "os" + "slices" "strconv" "strings" "time" @@ -79,8 +80,10 @@ type Config struct { ExternalBrowserTimeout time.Duration // Timeout for external browser login MaxRetryCount int // Specifies how many times non-periodic HTTP request can be retried - Application string // application name. - InsecureMode bool // driver doesn't check certificate revocation status + Application string // application name. + DisableOCSPChecks bool // driver doesn't check certificate revocation status + // Deprecated: InsecureMode use DisableOCSPChecks instead + InsecureMode bool OCSPFailOpen OCSPFailOpenMode // OCSP Fail Open Token string // Token to use for OAuth other forms of token based auth @@ -126,7 +129,7 @@ func (c *Config) Validate() error { // ocspMode returns the OCSP mode in string INSECURE, FAIL_OPEN, FAIL_CLOSED func (c *Config) ocspMode() string { - if c.InsecureMode { + if c.DisableOCSPChecks || c.InsecureMode { return ocspModeInsecure } else if c.OCSPFailOpen == ocspFailOpenNotSet || c.OCSPFailOpen == OCSPFailOpenTrue { // by default or set to true @@ -241,6 +244,9 @@ func DSN(cfg *Config) (dsn string, err error) { if cfg.InsecureMode { params.Add("insecureMode", strconv.FormatBool(cfg.InsecureMode)) } + if cfg.DisableOCSPChecks { + params.Add("disableOCSPChecks", strconv.FormatBool(cfg.DisableOCSPChecks)) + } if cfg.Tracing != "" { params.Add("tracing", cfg.Tracing) } @@ -637,7 +643,14 @@ func parseParams(cfg *Config, posQuestion int, dsn string) (err error) { // parseDSNParams parses the DSN "query string". Values must be url.QueryEscape'ed func parseDSNParams(cfg *Config, params string) (err error) { logger.Infof("Query String: %v\n", params) - for _, v := range strings.Split(params, "&") { + paramsSlice := strings.Split(params, "&") + insecureModeIdx := getFirstIndexOfWithPrefix(paramsSlice, "insecureMode") + disableOCSPChecksIdx := getFirstIndexOfWithPrefix(paramsSlice, "disableOCSPChecks") + if insecureModeIdx > -1 && disableOCSPChecksIdx > -1 { + logger.Warn("duplicated insecureMode and disableOCSPChecks. disableOCSPChecks takes precedence") + paramsSlice = slices.Concat(paramsSlice[:insecureModeIdx-1], paramsSlice[insecureModeIdx+1:]) + } + for _, v := range paramsSlice { param := strings.SplitN(v, "=", 2) if len(param) != 2 { continue @@ -715,12 +728,20 @@ func parseDSNParams(cfg *Config, params string) (err error) { return err } case "insecureMode": + logInsecureModeDeprecationInfo() var vv bool vv, err = strconv.ParseBool(value) if err != nil { return } cfg.InsecureMode = vv + case "disableOCSPChecks": + var vv bool + vv, err = strconv.ParseBool(value) + if err != nil { + return + } + cfg.DisableOCSPChecks = vv case "ocspFailOpen": var vv bool vv, err = strconv.ParseBool(value) @@ -839,6 +860,10 @@ func parseDSNParams(cfg *Config, params string) (err error) { return } +func logInsecureModeDeprecationInfo() { + logger.Warn("insecureMode is deprecated. Use disableOCSPChecks instead.") +} + func parseTimeout(value string) (time.Duration, error) { var vv int64 var err error diff --git a/dsn_test.go b/dsn_test.go index 7cf5b9975..ba6386d5d 100644 --- a/dsn_test.go +++ b/dsn_test.go @@ -540,6 +540,58 @@ func TestParseDSN(t *testing.T) { ocspMode: ocspModeInsecure, err: nil, }, + { + dsn: "u:p@a?database=d&schema=s&role=r&application=aa&authenticator=snowflake&disableOCSPChecks=true&passcode=pp&passcodeInPassword=true", + config: &Config{ + Account: "a", User: "u", Password: "p", + Protocol: "https", Host: "a.snowflakecomputing.com", Port: 443, + Database: "d", Schema: "s", Role: "r", Authenticator: AuthTypeSnowflake, Application: "aa", + DisableOCSPChecks: true, Passcode: "pp", PasscodeInPassword: true, + OCSPFailOpen: OCSPFailOpenTrue, + ValidateDefaultParameters: ConfigBoolTrue, + ClientTimeout: defaultClientTimeout, + JWTClientTimeout: defaultJWTClientTimeout, + ExternalBrowserTimeout: defaultExternalBrowserTimeout, + IncludeRetryReason: ConfigBoolTrue, + }, + ocspMode: ocspModeInsecure, + err: nil, + }, + { + dsn: "u:p@a?database=d&schema=s&role=r&application=aa&authenticator=snowflake&disableOCSPChecks=true&passcode=pp&passcodeInPassword=true", + config: &Config{ + Account: "a", User: "u", Password: "p", + Protocol: "https", Host: "a.snowflakecomputing.com", Port: 443, + Database: "d", Schema: "s", Role: "r", Authenticator: AuthTypeSnowflake, Application: "aa", + InsecureMode: false, DisableOCSPChecks: true, Passcode: "pp", PasscodeInPassword: true, + OCSPFailOpen: OCSPFailOpenTrue, + ValidateDefaultParameters: ConfigBoolTrue, + ClientTimeout: defaultClientTimeout, + JWTClientTimeout: defaultJWTClientTimeout, + ExternalBrowserTimeout: defaultExternalBrowserTimeout, + IncludeRetryReason: ConfigBoolTrue, + }, + ocspMode: ocspModeInsecure, + err: nil, + }, + // disableOCSPChecks should take precedence over insecureMode + { + dsn: "u:p@a?database=d&schema=s&role=r&application=aa&authenticator=snowflake&disableOCSPChecks=false&insecureMode=true&passcode=pp&passcodeInPassword=true", + config: &Config{ + Account: "a", User: "u", Password: "p", + Protocol: "https", Host: "a.snowflakecomputing.com", Port: 443, + Database: "d", Schema: "s", Role: "r", Authenticator: AuthTypeSnowflake, Application: "aa", + DisableOCSPChecks: false, Passcode: "pp", PasscodeInPassword: true, + OCSPFailOpen: OCSPFailOpenTrue, + ValidateDefaultParameters: ConfigBoolTrue, + ClientTimeout: defaultClientTimeout, + JWTClientTimeout: defaultJWTClientTimeout, + ExternalBrowserTimeout: defaultExternalBrowserTimeout, + IncludeRetryReason: ConfigBoolTrue, + }, + ocspMode: ocspModeFailOpen, + err: nil, + }, { // schema should be ignored as no value is specified. dsn: "u:p@a?database=d&schema", @@ -1412,6 +1464,35 @@ func TestDSN(t *testing.T) { }, dsn: "u:p@a.snowflakecomputing.com:443?insecureMode=true&ocspFailOpen=true&validateDefaultParameters=true", }, + { + cfg: &Config{ + User: "u", + Password: "p", + Account: "a", + DisableOCSPChecks: true, + }, + dsn: "u:p@a.snowflakecomputing.com:443?disableOCSPChecks=true&ocspFailOpen=true&validateDefaultParameters=true", + }, + { + cfg: &Config{ + User: "u", + Password: "p", + Account: "a", + InsecureMode: true, + DisableOCSPChecks: false, + }, + dsn: "u:p@a.snowflakecomputing.com:443?insecureMode=true&ocspFailOpen=true&validateDefaultParameters=true", + }, + { + cfg: &Config{ + User: "u", + Password: "p", + Account: "a", + InsecureMode: false, + DisableOCSPChecks: true, + }, + dsn: "u:p@a.snowflakecomputing.com:443?disableOCSPChecks=true&ocspFailOpen=true&validateDefaultParameters=true", + }, { cfg: &Config{ User: "u", diff --git a/ocsp.go b/ocsp.go index 2b82b3772..6a5b0c3ba 100644 --- a/ocsp.go +++ b/ocsp.go @@ -723,11 +723,7 @@ func canEarlyExitForOCSP(results []*ocspStatus, chainSize int) *ocspStatus { } } if len(msg) > 0 { - logger.Warnf( - "WARNING!!! Using fail-open to connect. Driver is connecting to an "+ - "HTTPS endpoint without OCSP based Certificate Revocation checking "+ - "as it could not obtain a valid OCSP Response to use from the CA OCSP "+ - "responder. Detail: %v", msg[1:]) + logger.Debugf("OCSP responder didn't respond correctly. Assuming certificate is not revoked. Detail: %v", msg[1:]) } return nil } diff --git a/util.go b/util.go index 53e474e4a..d6803f86d 100644 --- a/util.go +++ b/util.go @@ -339,3 +339,12 @@ func withLowerKeys[T any](in map[string]T) map[string]T { } return out } + +func getFirstIndexOfWithPrefix(in []string, prefix string) int { + for i, v := range in { + if strings.HasPrefix(v, prefix) { + return i + } + } + return -1 +} diff --git a/util_test.go b/util_test.go index 9a41cd42c..c617acd30 100644 --- a/util_test.go +++ b/util_test.go @@ -409,3 +409,14 @@ func TestWithLowerKeys(t *testing.T) { assertEqualE(t, lowerM["abc"], "def") assertEqualE(t, lowerM["ghi"], "KLM") } + +func TestGetFirstIndexOfWithPrefix(t *testing.T) { + nonEmpty := []string{"aaa", "bbb", "ccc"} + assertEqualE(t, getFirstIndexOfWithPrefix(nonEmpty, "a"), 0) + assertEqualE(t, getFirstIndexOfWithPrefix(nonEmpty, "aa"), 0) + assertEqualE(t, getFirstIndexOfWithPrefix(nonEmpty, "aaa"), 0) + assertEqualE(t, getFirstIndexOfWithPrefix(nonEmpty, "bb"), 1) + assertEqualE(t, getFirstIndexOfWithPrefix(nonEmpty, "ccc"), 2) + assertEqualE(t, getFirstIndexOfWithPrefix(nonEmpty, "dd"), -1) + assertEqualE(t, getFirstIndexOfWithPrefix([]string{}, "dd"), -1) +}