github.com/cornelk/go-cloud@v0.17.1/azure/azuredb/azuredb.go (about)

     1  // Copyright 2019 The Go Cloud Development Kit Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package azuredb contains Wire providers that are common across Azure Database.
    16  package azuredb
    17  
    18  import (
    19  	"context"
    20  	"crypto/x509"
    21  	"encoding/pem"
    22  	"fmt"
    23  	"io"
    24  	"io/ioutil"
    25  	"net/http"
    26  
    27  	"golang.org/x/net/context/ctxhttp"
    28  )
    29  
    30  const caBundleURL = "https://www.digicert.com/CACerts/BaltimoreCyberTrustRoot.crt.pem"
    31  
    32  // A CertPoolProvider returns a certificate pool that contains the Azure CA certificate.
    33  type CertPoolProvider interface {
    34  	AzureCertPool(context.Context) (*x509.CertPool, error)
    35  }
    36  
    37  // CertFetcher pulls the Azure CA certificates from Digicert's servers. The zero
    38  // value will fetch certificates using the default HTTP client.
    39  type CertFetcher struct {
    40  	// Client is the HTTP client used to make requests. If nil, then
    41  	// http.DefaultClient is used.
    42  	Client *http.Client
    43  }
    44  
    45  // AzureCertPool fetches the Azure CA certificates and places them into a pool.
    46  // It is safe to call from multiple goroutines.
    47  func (cf *CertFetcher) AzureCertPool(ctx context.Context) (*x509.CertPool, error) {
    48  	certs, err := cf.Fetch(ctx)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	certPool := x509.NewCertPool()
    53  	for _, c := range certs {
    54  		certPool.AddCert(c)
    55  	}
    56  	return certPool, nil
    57  }
    58  
    59  // Fetch fetches the Azure CA certificates. It is safe to call from multiple goroutines.
    60  func (cf *CertFetcher) Fetch(ctx context.Context) ([]*x509.Certificate, error) {
    61  	client := cf.Client
    62  	if client == nil {
    63  		client = http.DefaultClient
    64  	}
    65  	resp, err := ctxhttp.Get(ctx, client, caBundleURL)
    66  	if err != nil {
    67  		return nil, fmt.Errorf("fetch Azure certificates: %v", err)
    68  	}
    69  	defer resp.Body.Close()
    70  	if resp.StatusCode != http.StatusOK {
    71  		return nil, fmt.Errorf("fetch Azure certificates: HTTP %s", resp.Status)
    72  	}
    73  	pemData, err := ioutil.ReadAll(&io.LimitedReader{R: resp.Body, N: 1 << 20}) // limit to 1MiB
    74  	if err != nil {
    75  		return nil, fmt.Errorf("fetch Azure certificates: %v", err)
    76  	}
    77  	var certs []*x509.Certificate
    78  	for len(pemData) > 0 {
    79  		var block *pem.Block
    80  		block, pemData = pem.Decode(pemData)
    81  		if block == nil {
    82  			break
    83  		}
    84  		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
    85  			continue
    86  		}
    87  		c, err := x509.ParseCertificate(block.Bytes)
    88  		if err != nil {
    89  			return nil, fmt.Errorf("fetch Azure certificates: %v", err)
    90  		}
    91  		certs = append(certs, c)
    92  	}
    93  	return certs, nil
    94  }