Skip to content

Commit 894e78c

Browse files
Snow 893925 ocsp responders in privatelink (#915)
Co-authored-by: Joyce Ling <[email protected]>
1 parent 082e9f8 commit 894e78c

6 files changed

+139
-13
lines changed

arrow_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"time"
1414
)
1515

16-
//A test just to show Snowflake version
16+
// A test just to show Snowflake version
1717
func TestCheckVersion(t *testing.T) {
1818
conn := openConn(t)
1919
defer conn.Close()

connection_util.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -281,11 +281,13 @@ func populateChunkDownloader(
281281

282282
func (sc *snowflakeConn) setupOCSPPrivatelink(app string, host string) error {
283283
ocspCacheServer := fmt.Sprintf("http://ocsp.%v/ocsp_response_cache.json", host)
284+
logger.Debugf("OCSP Cache Server for Privatelink: %v\n", ocspCacheServer)
284285
if err := os.Setenv(cacheServerURLEnv, ocspCacheServer); err != nil {
285286
return err
286287
}
287-
ocspRetryHost := fmt.Sprintf("http://ocsp.%v/retry/", host) + "%v/%v"
288-
if err := os.Setenv(ocspRetryURLEnv, ocspRetryHost); err != nil {
288+
ocspRetryHostTemplate := fmt.Sprintf("http://ocsp.%v/retry/", host) + "%v/%v"
289+
logger.Debugf("OCSP Retry URL for Privatelink: %v\n", ocspRetryHostTemplate)
290+
if err := os.Setenv(ocspRetryURLEnv, ocspRetryHostTemplate); err != nil {
289291
return err
290292
}
291293
return nil

data1.txt.gz

Whitespace-only changes.

driver_ocsp_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func cleanup() {
4949
unsetenv(ocspTestResponderTimeoutEnv)
5050
unsetenv(ocspTestResponderURLEnv)
5151
unsetenv(ocspTestNoOCSPURLEnv)
52+
unsetenv(ocspRetryURLEnv)
5253
unsetenv(cacheDirEnv)
5354
}
5455

ocsp.go

+95-10
Original file line numberDiff line numberDiff line change
@@ -360,14 +360,14 @@ func checkOCSPCacheServer(
360360
headers := make(map[string]string)
361361
res, err := newRetryHTTP(ctx, client, req, ocspServerHost, headers, totalTimeout, defaultTimeProvider).execute()
362362
if err != nil {
363-
logger.Errorf("failed to get OCSP cache from OCSP Cache Server. %v\n", err)
363+
logger.Errorf("failed to get OCSP cache from OCSP Cache Server. %v", err)
364364
return nil, &ocspStatus{
365365
code: ocspFailedSubmit,
366366
err: err,
367367
}
368368
}
369369
defer res.Body.Close()
370-
logger.Debugf("StatusCode from OCSP Cache Server: %v\n", res.StatusCode)
370+
logger.Debugf("StatusCode from OCSP Cache Server: %v", res.StatusCode)
371371
if res.StatusCode != http.StatusOK {
372372
return nil, &ocspStatus{
373373
code: ocspFailedResponse,
@@ -381,7 +381,7 @@ func checkOCSPCacheServer(
381381
if err := dec.Decode(&respd); err == io.EOF {
382382
break
383383
} else if err != nil {
384-
logger.Errorf("failed to decode OCSP cache. %v\n", err)
384+
logger.Errorf("failed to decode OCSP cache. %v", err)
385385
return nil, &ocspStatus{
386386
code: ocspFailedExtractResponse,
387387
err: err,
@@ -428,15 +428,66 @@ func retryOCSP(
428428
err: fmt.Errorf("HTTP code is not OK. %v: %v", res.StatusCode, res.Status),
429429
}
430430
}
431-
logger.Debug("reading contents")
432431
ocspResBytes, err = io.ReadAll(res.Body)
433432
if err != nil {
434433
return ocspRes, ocspResBytes, &ocspStatus{
435434
code: ocspFailedExtractResponse,
436435
err: err,
437436
}
438437
}
439-
logger.Debug("parsing OCSP response")
438+
ocspRes, err = ocsp.ParseResponse(ocspResBytes, issuer)
439+
if err != nil {
440+
logger.Warnf("error when parsing ocsp response: %v", err)
441+
logger.Warnf("performing GET fallback request to OCSP")
442+
return fallbackRetryOCSPToGETRequest(ctx, client, req, ocspHost, headers, issuer, totalTimeout)
443+
}
444+
445+
logger.Debugf("OCSP Status from server: %v", printStatus(ocspRes))
446+
return ocspRes, ocspResBytes, &ocspStatus{
447+
code: ocspSuccess,
448+
}
449+
}
450+
451+
// fallbackRetryOCSPToGETRequest is the third level of retry method. Some OCSP responders do not support POST requests
452+
// and will return with a "malformed" request error. In that case we also try to perform a GET request
453+
func fallbackRetryOCSPToGETRequest(
454+
ctx context.Context,
455+
client clientInterface,
456+
req requestFunc,
457+
ocspHost *url.URL,
458+
headers map[string]string,
459+
issuer *x509.Certificate,
460+
totalTimeout time.Duration) (
461+
ocspRes *ocsp.Response,
462+
ocspResBytes []byte,
463+
ocspS *ocspStatus) {
464+
multiplier := 1
465+
if atomic.LoadUint32((*uint32)(&ocspFailOpen)) == (uint32)(OCSPFailOpenFalse) {
466+
multiplier = 3 // up to 3 times for Fail Close mode
467+
}
468+
res, err := newRetryHTTP(ctx, client, req, ocspHost, headers,
469+
totalTimeout*time.Duration(multiplier), defaultTimeProvider).execute()
470+
if err != nil {
471+
return ocspRes, ocspResBytes, &ocspStatus{
472+
code: ocspFailedSubmit,
473+
err: err,
474+
}
475+
}
476+
defer res.Body.Close()
477+
logger.Debugf("GET fallback StatusCode from OCSP Server: %v", res.StatusCode)
478+
if res.StatusCode != http.StatusOK {
479+
return ocspRes, ocspResBytes, &ocspStatus{
480+
code: ocspFailedResponse,
481+
err: fmt.Errorf("HTTP code is not OK. %v: %v", res.StatusCode, res.Status),
482+
}
483+
}
484+
ocspResBytes, err = io.ReadAll(res.Body)
485+
if err != nil {
486+
return ocspRes, ocspResBytes, &ocspStatus{
487+
code: ocspFailedExtractResponse,
488+
err: err,
489+
}
490+
}
440491
ocspRes, err = ocsp.ParseResponse(ocspResBytes, issuer)
441492
if err != nil {
442493
return ocspRes, ocspResBytes, &ocspStatus{
@@ -445,14 +496,39 @@ func retryOCSP(
445496
}
446497
}
447498

499+
logger.Debugf("GET fallback OCSP Status from server: %v", printStatus(ocspRes))
448500
return ocspRes, ocspResBytes, &ocspStatus{
449501
code: ocspSuccess,
450502
}
451503
}
452504

505+
func printStatus(response *ocsp.Response) string {
506+
switch response.Status {
507+
case ocsp.Good:
508+
return "Good"
509+
case ocsp.Revoked:
510+
return "Revoked"
511+
case ocsp.Unknown:
512+
return "Unknown"
513+
default:
514+
return fmt.Sprintf("%d", response.Status)
515+
}
516+
}
517+
518+
func fullOCSPURL(url *url.URL) string {
519+
fullURL := url.Hostname()
520+
if url.Path != "" {
521+
if !strings.HasPrefix(url.Path, "/") {
522+
fullURL += "/"
523+
}
524+
fullURL += url.Path
525+
}
526+
return fullURL
527+
}
528+
453529
// getRevocationStatus checks the certificate revocation status for subject using issuer certificate.
454530
func getRevocationStatus(ctx context.Context, subject, issuer *x509.Certificate) *ocspStatus {
455-
logger.Infof("Subject: %v, Issuer: %v\n", subject.Subject, issuer.Subject)
531+
logger.Infof("Subject: %v, Issuer: %v", subject.Subject, issuer.Subject)
456532

457533
status, ocspReq, encodedCertID := validateWithCache(subject, issuer)
458534
if isValidOCSPStatus(status.code) {
@@ -461,8 +537,8 @@ func getRevocationStatus(ctx context.Context, subject, issuer *x509.Certificate)
461537
if ocspReq == nil || encodedCertID == nil {
462538
return status
463539
}
464-
logger.Infof("cache missed\n")
465-
logger.Infof("OCSP Server: %v\n", subject.OCSPServer)
540+
logger.Infof("cache missed")
541+
logger.Infof("OCSP Server: %v", subject.OCSPServer)
466542
if len(subject.OCSPServer) == 0 || isTestNoOCSPURL() {
467543
return &ocspStatus{
468544
code: ocspNoServer,
@@ -484,9 +560,14 @@ func getRevocationStatus(ctx context.Context, subject, issuer *x509.Certificate)
484560
hostnameStr := os.Getenv(ocspTestResponderURLEnv)
485561
var hostname string
486562
if retryURL := os.Getenv(ocspRetryURLEnv); retryURL != "" {
487-
hostname = fmt.Sprintf(retryURL, u.Hostname(), base64.StdEncoding.EncodeToString(ocspReq))
563+
hostname = fmt.Sprintf(retryURL, fullOCSPURL(u), base64.StdEncoding.EncodeToString(ocspReq))
564+
u0, err := url.Parse(hostname)
565+
if err == nil {
566+
hostname = u0.Hostname()
567+
u = u0
568+
}
488569
} else {
489-
hostname = u.Hostname()
570+
hostname = fullOCSPURL(u)
490571
}
491572
if hostnameStr != "" {
492573
u0, err := url.Parse(hostnameStr)
@@ -495,6 +576,10 @@ func getRevocationStatus(ctx context.Context, subject, issuer *x509.Certificate)
495576
u = u0
496577
}
497578
}
579+
580+
logger.Debugf("Fetching OCSP response from server: %v", u)
581+
logger.Debugf("Host in headers: %v", hostname)
582+
498583
headers := make(map[string]string)
499584
headers[httpHeaderContentType] = "application/ocsp-request"
500585
headers[httpHeaderAccept] = "application/ocsp-response"

ocsp_test.go

+38
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,44 @@ func TestOCSPRetry(t *testing.T) {
340340
}
341341
}
342342

343+
func TestFullOCSPURL(t *testing.T) {
344+
testcases := []tcFullOCSPURL{
345+
{
346+
url: &url.URL{Host: "some-ocsp-url.com"},
347+
expectedURLString: "some-ocsp-url.com",
348+
},
349+
{
350+
url: &url.URL{
351+
Host: "some-ocsp-url.com",
352+
Path: "/some-path",
353+
},
354+
expectedURLString: "some-ocsp-url.com/some-path",
355+
},
356+
{
357+
url: &url.URL{
358+
Host: "some-ocsp-url.com",
359+
Path: "some-path",
360+
},
361+
expectedURLString: "some-ocsp-url.com/some-path",
362+
},
363+
}
364+
365+
for _, testcase := range testcases {
366+
t.Run("", func(t *testing.T) {
367+
returnedStringURL := fullOCSPURL(testcase.url)
368+
if returnedStringURL != testcase.expectedURLString {
369+
t.Fatalf("failed to match returned OCSP url string; expected: %v, got: %v",
370+
testcase.expectedURLString, returnedStringURL)
371+
}
372+
})
373+
}
374+
}
375+
376+
type tcFullOCSPURL struct {
377+
url *url.URL
378+
expectedURLString string
379+
}
380+
343381
func TestOCSPCacheServerRetry(t *testing.T) {
344382
dummyOCSPHost := &url.URL{
345383
Scheme: "https",

0 commit comments

Comments
 (0)