Skip to content

Commit

Permalink
Work around an azure HTTP/2 bug (#183)
Browse files Browse the repository at this point in the history
* Work around an azure HTTP/2 butg

* fix/chore (azurevault): simple fix for go.sum

go mod tidy, so the tests stop failing

* test (azurevault): add withCertPool to getKeyVaultClient(...)

This change allows us to write some unit tests that use
a self-signed cert using httptest.

* test (azurevault): add unit tests for getKeyVaultClient(...)

---------

Co-authored-by: Jim <jlambert@hashicorp.com>
  • Loading branch information
sgmiller and jimlambrt committed Sep 5, 2023
1 parent 7a966e6 commit a8fa5b8
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 2 deletions.
45 changes: 43 additions & 2 deletions wrappers/azurekeyvault/azurekeyvault.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ package azurekeyvault

import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"errors"
"fmt"
"net"
"net/http"
"os"
"strings"
"sync/atomic"
"time"

"golang.org/x/net/http2"

"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
"github.com/Azure/go-autorest/autorest"
Expand Down Expand Up @@ -159,7 +166,7 @@ func (v *Wrapper) SetConfig(_ context.Context, opt ...wrapping.Option) (*wrappin
v.baseURL = v.buildBaseURL()

if v.client == nil {
client, err := v.getKeyVaultClient()
client, err := v.getKeyVaultClient(nil)
if err != nil {
return nil, fmt.Errorf("error initializing Azure Key Vault wrapper client: %w", err)
}
Expand Down Expand Up @@ -295,7 +302,7 @@ func (v *Wrapper) buildBaseURL() string {
return fmt.Sprintf("https://%s.%s/", v.vaultName, v.environment.KeyVaultDNSSuffix)
}

func (v *Wrapper) getKeyVaultClient() (*keyvault.BaseClient, error) {
func (v *Wrapper) getKeyVaultClient(withCertPool *x509.CertPool) (*keyvault.BaseClient, error) {
var authorizer autorest.Authorizer
var err error

Expand All @@ -321,8 +328,42 @@ func (v *Wrapper) getKeyVaultClient() (*keyvault.BaseClient, error) {
}
}

dialer := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}
customTransport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: dialer.DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
Renegotiation: tls.RenegotiateFreelyAsClient,
RootCAs: withCertPool,
},
}
if http2Transport, err := http2.ConfigureTransports(customTransport); err == nil {
// if the connection has been idle for 10 seconds, send a ping frame for a health check
http2Transport.ReadIdleTimeout = 10 * time.Second
// if there's no response to the ping within 2 seconds, close the connection
http2Transport.PingTimeout = 2 * time.Second
}

client := keyvault.New()
client.Authorizer = authorizer
client.SendDecorators = append(client.SendDecorators, func(s autorest.Sender) autorest.Sender {
if ar, ok := s.(autorest.Client); ok {
ar.Sender = &http.Client{
Transport: customTransport,
}
return ar
}
return s
})
return &client, nil
}

Expand Down
67 changes: 67 additions & 0 deletions wrappers/azurekeyvault/azurekeyvault_acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ package azurekeyvault

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
"net/http/httptest"
"os"
"reflect"
"testing"

"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -102,3 +108,64 @@ func TestAzureKeyVault_Lifecycle(t *testing.T) {
t.Fatalf("expected %s, got %s", input, pt)
}
}

func Test_getKeyVaultClient(t *testing.T) {
t.Parallel()
config := map[string]string{
"disallow_env_vars": "true",
"tenant_id": "a-tenant-id",
"client_id": "a-client-id",
"client_secret": "a-client-secret",
"environment": azure.PublicCloud.Name,
"resource": "a-resource",
"vault_name": "a-vault-name",
"key_name": "a-key-name",
}
s := NewWrapper()
_, err := s.SetConfig(
context.Background(),
wrapping.WithConfigMap(config),
WithKeyNotRequired(true),
)
require.NoError(t, err)
t.Run("send-decorators-set", func(t *testing.T) {
// let's at least ensure that the custom SendDecorator is being properly
// set.
t.Parallel()
got, err := s.getKeyVaultClient(nil)
require.NoError(t, err)
assert.NotEmpty(t, got.SendDecorators)
})
t.Run("force-tls-error", func(t *testing.T) {
// not great, but this test will at least ensure that the client's
// custom TLS transport is being used
t.Parallel()
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(fmt.Sprintf("version: %s", tls.VersionName(r.TLS.Version))))
}))
ts.TLS = &tls.Config{
MinVersion: tls.VersionTLS10,
MaxVersion: tls.VersionTLS10,
}
ts.StartTLS()
defer ts.Close()

certPool := x509.NewCertPool()
certPool.AddCert(ts.Certificate())

assert.NoError(t, err)
client, err := s.getKeyVaultClient(certPool)
require.NoError(t, err)
assert.NotEmpty(t, client.SendDecorators)
client.Authorizer = &authorizer{}
_, err = client.GetKey(context.Background(), ts.URL, "global", "1")
require.Error(t, err)
assert.ErrorContains(t, err, "tls: protocol version not supported")
})
}

type authorizer struct{}

func (*authorizer) WithAuthorization() autorest.PrepareDecorator {
return autorest.WithNothing()
}
2 changes: 2 additions & 0 deletions wrappers/azurekeyvault/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/hashicorp/go-hclog v1.4.0
github.com/hashicorp/go-kms-wrapping/v2 v2.0.9-0.20230228100945-740d2999c798
github.com/stretchr/testify v1.8.2
golang.org/x/net v0.6.0
)

require (
Expand All @@ -33,6 +34,7 @@ require (
github.com/rogpeppe/go-internal v1.6.1 // indirect
golang.org/x/crypto v0.6.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
4 changes: 4 additions & 0 deletions wrappers/azurekeyvault/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -99,6 +101,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
Expand Down

0 comments on commit a8fa5b8

Please sign in to comment.