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

Snow 893925 ocsp responders in privatelink #915

Merged
merged 11 commits into from
Sep 20, 2023
2 changes: 1 addition & 1 deletion arrow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"time"
)

//A test just to show Snowflake version
// A test just to show Snowflake version
func TestCheckVersion(t *testing.T) {
conn := openConn(t)
defer conn.Close()
Expand Down
6 changes: 4 additions & 2 deletions connection_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,13 @@ func populateChunkDownloader(

func (sc *snowflakeConn) setupOCSPPrivatelink(app string, host string) error {
ocspCacheServer := fmt.Sprintf("http://ocsp.%v/ocsp_response_cache.json", host)
logger.Debugf("OCSP Cache Server for Privatelink: %v\n", ocspCacheServer)
if err := os.Setenv(cacheServerURLEnv, ocspCacheServer); err != nil {
return err
}
ocspRetryHost := fmt.Sprintf("http://ocsp.%v/retry/", host) + "%v/%v"
if err := os.Setenv(ocspRetryURLEnv, ocspRetryHost); err != nil {
ocspRetryHostTemplate := fmt.Sprintf("http://ocsp.%v/retry/", host) + "%v/%v"
logger.Debugf("OCSP Retry URL for Privatelink: %v\n", ocspRetryHostTemplate)
if err := os.Setenv(ocspRetryURLEnv, ocspRetryHostTemplate); err != nil {
return err
}
return nil
Expand Down
Empty file added data1.txt.gz
Empty file.
1 change: 1 addition & 0 deletions driver_ocsp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func cleanup() {
unsetenv(ocspTestResponderTimeoutEnv)
unsetenv(ocspTestResponderURLEnv)
unsetenv(ocspTestNoOCSPURLEnv)
unsetenv(ocspRetryURLEnv)
unsetenv(cacheDirEnv)
}

Expand Down
105 changes: 95 additions & 10 deletions ocsp.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,14 +360,14 @@ func checkOCSPCacheServer(
headers := make(map[string]string)
res, err := newRetryHTTP(ctx, client, req, ocspServerHost, headers, totalTimeout, defaultTimeProvider).execute()
if err != nil {
logger.Errorf("failed to get OCSP cache from OCSP Cache Server. %v\n", err)
logger.Errorf("failed to get OCSP cache from OCSP Cache Server. %v", err)
return nil, &ocspStatus{
code: ocspFailedSubmit,
err: err,
}
}
defer res.Body.Close()
logger.Debugf("StatusCode from OCSP Cache Server: %v\n", res.StatusCode)
logger.Debugf("StatusCode from OCSP Cache Server: %v", res.StatusCode)
if res.StatusCode != http.StatusOK {
return nil, &ocspStatus{
code: ocspFailedResponse,
Expand All @@ -381,7 +381,7 @@ func checkOCSPCacheServer(
if err := dec.Decode(&respd); err == io.EOF {
break
} else if err != nil {
logger.Errorf("failed to decode OCSP cache. %v\n", err)
logger.Errorf("failed to decode OCSP cache. %v", err)
return nil, &ocspStatus{
code: ocspFailedExtractResponse,
err: err,
Expand Down Expand Up @@ -428,15 +428,66 @@ func retryOCSP(
err: fmt.Errorf("HTTP code is not OK. %v: %v", res.StatusCode, res.Status),
}
}
logger.Debug("reading contents")
ocspResBytes, err = io.ReadAll(res.Body)
if err != nil {
return ocspRes, ocspResBytes, &ocspStatus{
code: ocspFailedExtractResponse,
err: err,
}
}
logger.Debug("parsing OCSP response")
ocspRes, err = ocsp.ParseResponse(ocspResBytes, issuer)
sfc-gh-dprzybysz marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
logger.Warnf("error when parsing ocsp response: %v", err)
logger.Warnf("performing GET fallback request to OCSP")
return fallbackRetryOCSPToGETRequest(ctx, client, req, ocspHost, headers, issuer, totalTimeout)
}

logger.Debugf("OCSP Status from server: %v", printStatus(ocspRes))
return ocspRes, ocspResBytes, &ocspStatus{
code: ocspSuccess,
}
}

// fallbackRetryOCSPToGETRequest is the third level of retry method. Some OCSP responders do not support POST requests
// and will return with a "malformed" request error. In that case we also try to perform a GET request
func fallbackRetryOCSPToGETRequest(
ctx context.Context,
client clientInterface,
req requestFunc,
ocspHost *url.URL,
headers map[string]string,
issuer *x509.Certificate,
totalTimeout time.Duration) (
ocspRes *ocsp.Response,
ocspResBytes []byte,
ocspS *ocspStatus) {
multiplier := 1
if atomic.LoadUint32((*uint32)(&ocspFailOpen)) == (uint32)(OCSPFailOpenFalse) {
multiplier = 3 // up to 3 times for Fail Close mode
}
res, err := newRetryHTTP(ctx, client, req, ocspHost, headers,
totalTimeout*time.Duration(multiplier), defaultTimeProvider).execute()
if err != nil {
return ocspRes, ocspResBytes, &ocspStatus{
code: ocspFailedSubmit,
err: err,
}
}
defer res.Body.Close()
logger.Debugf("GET fallback StatusCode from OCSP Server: %v", res.StatusCode)
if res.StatusCode != http.StatusOK {
return ocspRes, ocspResBytes, &ocspStatus{
code: ocspFailedResponse,
err: fmt.Errorf("HTTP code is not OK. %v: %v", res.StatusCode, res.Status),
}
}
ocspResBytes, err = io.ReadAll(res.Body)
if err != nil {
return ocspRes, ocspResBytes, &ocspStatus{
code: ocspFailedExtractResponse,
err: err,
}
}
ocspRes, err = ocsp.ParseResponse(ocspResBytes, issuer)
if err != nil {
return ocspRes, ocspResBytes, &ocspStatus{
Expand All @@ -445,14 +496,39 @@ func retryOCSP(
}
}

logger.Debugf("GET fallback OCSP Status from server: %v", printStatus(ocspRes))
return ocspRes, ocspResBytes, &ocspStatus{
code: ocspSuccess,
}
}

func printStatus(response *ocsp.Response) string {
switch response.Status {
case ocsp.Good:
return "Good"
case ocsp.Revoked:
return "Revoked"
case ocsp.Unknown:
return "Unknown"
default:
return fmt.Sprintf("%d", response.Status)
}
}

func fullOCSPURL(url *url.URL) string {
fullURL := url.Hostname()
if url.Path != "" {
if !strings.HasPrefix(url.Path, "/") {
fullURL += "/"
}
fullURL += url.Path
sfc-gh-dheyman marked this conversation as resolved.
Show resolved Hide resolved
}
return fullURL
}

// getRevocationStatus checks the certificate revocation status for subject using issuer certificate.
func getRevocationStatus(ctx context.Context, subject, issuer *x509.Certificate) *ocspStatus {
logger.Infof("Subject: %v, Issuer: %v\n", subject.Subject, issuer.Subject)
logger.Infof("Subject: %v, Issuer: %v", subject.Subject, issuer.Subject)

status, ocspReq, encodedCertID := validateWithCache(subject, issuer)
if isValidOCSPStatus(status.code) {
Expand All @@ -461,8 +537,8 @@ func getRevocationStatus(ctx context.Context, subject, issuer *x509.Certificate)
if ocspReq == nil || encodedCertID == nil {
return status
}
logger.Infof("cache missed\n")
logger.Infof("OCSP Server: %v\n", subject.OCSPServer)
logger.Infof("cache missed")
logger.Infof("OCSP Server: %v", subject.OCSPServer)
if len(subject.OCSPServer) == 0 || isTestNoOCSPURL() {
return &ocspStatus{
code: ocspNoServer,
Expand All @@ -484,9 +560,14 @@ func getRevocationStatus(ctx context.Context, subject, issuer *x509.Certificate)
hostnameStr := os.Getenv(ocspTestResponderURLEnv)
var hostname string
if retryURL := os.Getenv(ocspRetryURLEnv); retryURL != "" {
hostname = fmt.Sprintf(retryURL, u.Hostname(), base64.StdEncoding.EncodeToString(ocspReq))
hostname = fmt.Sprintf(retryURL, fullOCSPURL(u), base64.StdEncoding.EncodeToString(ocspReq))
sfc-gh-dheyman marked this conversation as resolved.
Show resolved Hide resolved
u0, err := url.Parse(hostname)
if err == nil {
hostname = u0.Hostname()
u = u0
}
} else {
hostname = u.Hostname()
hostname = fullOCSPURL(u)
}
if hostnameStr != "" {
u0, err := url.Parse(hostnameStr)
Expand All @@ -495,6 +576,10 @@ func getRevocationStatus(ctx context.Context, subject, issuer *x509.Certificate)
u = u0
}
}

logger.Debugf("Fetching OCSP response from server: %v", u)
logger.Debugf("Host in headers: %v", hostname)

headers := make(map[string]string)
headers[httpHeaderContentType] = "application/ocsp-request"
headers[httpHeaderAccept] = "application/ocsp-response"
Expand Down
38 changes: 38 additions & 0 deletions ocsp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,44 @@ func TestOCSPRetry(t *testing.T) {
}
}

func TestFullOCSPURL(t *testing.T) {
testcases := []tcFullOCSPURL{
{
url: &url.URL{Host: "some-ocsp-url.com"},
expectedURLString: "some-ocsp-url.com",
},
{
url: &url.URL{
Host: "some-ocsp-url.com",
Path: "/some-path",
},
expectedURLString: "some-ocsp-url.com/some-path",
},
{
url: &url.URL{
Host: "some-ocsp-url.com",
Path: "some-path",
},
expectedURLString: "some-ocsp-url.com/some-path",
},
}

for _, testcase := range testcases {
t.Run("", func(t *testing.T) {
returnedStringURL := fullOCSPURL(testcase.url)
if returnedStringURL != testcase.expectedURLString {
t.Fatalf("failed to match returned OCSP url string; expected: %v, got: %v",
testcase.expectedURLString, returnedStringURL)
}
})
}
}

type tcFullOCSPURL struct {
url *url.URL
expectedURLString string
}

func TestOCSPCacheServerRetry(t *testing.T) {
dummyOCSPHost := &url.URL{
Scheme: "https",
Expand Down
Loading