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

Update HTTP transport wrapper to support TLSConfig cloning #1926

Merged
merged 5 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ require (
github.com/hashicorp/vault-plugin-auth-jwt v0.13.2-0.20221012184020-28cc68ee722b
github.com/hashicorp/vault-plugin-auth-kerberos v0.8.0
github.com/hashicorp/vault-plugin-auth-oci v0.13.0-pre
github.com/hashicorp/vault/api v1.8.1
github.com/hashicorp/vault/api v1.9.3-0.20230628215639-3ca33976762c
github.com/hashicorp/vault/sdk v0.6.0
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/jcmturner/gokrb5/v8 v8.4.2
Expand All @@ -46,7 +46,7 @@ require (
github.com/mitchellh/mapstructure v1.5.0
github.com/mitchellh/pointerstructure v1.2.1 // indirect
go.uber.org/atomic v1.10.0 // indirect
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a
golang.org/x/crypto v0.6.0
golang.org/x/net v0.7.0
golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
Expand Down
12 changes: 8 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
Expand Down Expand Up @@ -1238,8 +1240,8 @@ github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFy
github.com/hashicorp/vault/api v1.5.0/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM=
github.com/hashicorp/vault/api v1.7.2/go.mod h1:xbfA+1AvxFseDzxxdWaL0uO99n1+tndus4GCrtouy0M=
github.com/hashicorp/vault/api v1.8.0/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E=
github.com/hashicorp/vault/api v1.8.1 h1:bMieWIe6dAlqAAPReZO/8zYtXaWUg/21umwqGZpEjCI=
github.com/hashicorp/vault/api v1.8.1/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E=
github.com/hashicorp/vault/api v1.9.3-0.20230628215639-3ca33976762c h1:JhuUjg3NzAQczN4/ji+JltY06545IAJI+djmdDBpHUo=
github.com/hashicorp/vault/api v1.9.3-0.20230628215639-3ca33976762c/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
github.com/hashicorp/vault/api/auth/approle v0.1.0/go.mod h1:mHOLgh//xDx4dpqXoq6tS8Ob0FoCFWLU2ibJ26Lfmag=
github.com/hashicorp/vault/api/auth/kubernetes v0.2.0/go.mod h1:2BKADs9mwqAycDK/6tiHRh2sX0SPnC0DN4wHjJoAirw=
github.com/hashicorp/vault/api/auth/userpass v0.1.0/go.mod h1:0orUbtkEwbEPmaQ+wvfrOddGBimLJnuN8A/J0PNfBks=
Expand Down Expand Up @@ -1996,6 +1998,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
Expand Down Expand Up @@ -2023,8 +2026,8 @@ golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg=
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -2156,6 +2159,7 @@ golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
Expand Down
29 changes: 23 additions & 6 deletions helper/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ package helper

import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"log"
"net/http"
"net/http/httputil"
"os"
"strconv"
"strings"
"sync"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging"
"github.com/hashicorp/vault/sdk/helper/salt"
Expand All @@ -29,7 +32,7 @@ const (
EnvLogResponseBody = "TERRAFORM_VAULT_LOG_RESPONSE_BODY"
)

// TransportOptions for transport.
// TransportOptions for TransportWrapper.
type TransportOptions struct {
// HMACRequestHeaders ensure that any configured header's value is
// never revealed during logging operations.
Expand All @@ -42,7 +45,7 @@ type TransportOptions struct {
LogResponseBody bool
}

// DefaultTransportOptions for setting up the HTTP transport wrapper.
// DefaultTransportOptions for setting up the HTTP TransportWrapper wrapper.
func DefaultTransportOptions() *TransportOptions {
opts := &TransportOptions{
HMACRequestHeaders: []string{
Expand All @@ -65,13 +68,27 @@ func DefaultTransportOptions() *TransportOptions {
return opts
}

type transport struct {
type TransportWrapper struct {
name string
transport http.RoundTripper
options *TransportOptions
m sync.RWMutex
}

func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
func (t *TransportWrapper) SetTLSConfig(c *tls.Config) error {
t.m.Lock()
defer t.m.Unlock()
transport, ok := t.transport.(*http.Transport)
if !ok {
return fmt.Errorf("type assertion failed for %T", t.transport)
}

transport.TLSClientConfig = c

return nil
}

func (t *TransportWrapper) RoundTrip(req *http.Request) (*http.Response, error) {
if logging.IsDebugOrHigher() {
var origHeaders http.Header
if len(t.options.HMACRequestHeaders) > 0 && len(req.Header) > 0 {
Expand Down Expand Up @@ -118,8 +135,8 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
return resp, nil
}

func NewTransport(name string, t http.RoundTripper, opts *TransportOptions) *transport {
return &transport{
func NewTransport(name string, t http.RoundTripper, opts *TransportOptions) *TransportWrapper {
return &TransportWrapper{
name: name,
transport: t,
options: opts,
Expand Down
18 changes: 15 additions & 3 deletions internal/provider/auth_azure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,11 @@ func TestAuthLoginAzure_LoginPath(t *testing.T) {
func TestAuthLoginAzure_Login(t *testing.T) {
handlerFunc := func(t *testLoginHandler, w http.ResponseWriter, req *http.Request) {
m, err := json.Marshal(
&api.Secret{},
&api.Secret{
Data: map[string]interface{}{
"auth_login": "azure",
},
},
)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
Expand Down Expand Up @@ -198,7 +202,11 @@ func TestAuthLoginAzure_Login(t *testing.T) {
consts.FieldResourceGroupName: "res1",
},
},
want: &api.Secret{},
want: &api.Secret{
vinay-gopalan marked this conversation as resolved.
Show resolved Hide resolved
Data: map[string]interface{}{
"auth_login": "azure",
},
},
wantErr: false,
},
{
Expand All @@ -225,7 +233,11 @@ func TestAuthLoginAzure_Login(t *testing.T) {
skipFunc: func(t *testing.T) {
testutil.SkipTestEnvUnset(t, envVarTFAccAzureAuth)
},
want: &api.Secret{},
want: &api.Secret{
Data: map[string]interface{}{
"auth_login": "azure",
},
},
wantErr: false,
},
{
Expand Down
55 changes: 30 additions & 25 deletions internal/provider/auth_cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
package provider

import (
"crypto/tls"
"fmt"
"net/http"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/vault/api"

"github.com/hashicorp/terraform-provider-vault/helper"
"github.com/hashicorp/terraform-provider-vault/internal/consts"
)

Expand Down Expand Up @@ -75,7 +78,11 @@ func (l *AuthLoginCert) LoginPath() string {
}

func (l *AuthLoginCert) Init(d *schema.ResourceData, authField string) (AuthLogin, error) {
if err := l.AuthLoginCommon.Init(d, authField); err != nil {
if err := l.AuthLoginCommon.Init(d, authField,
func(data *schema.ResourceData) error {
return l.checkRequiredFields(d, consts.FieldCertFile, consts.FieldKeyFile)
},
); err != nil {
return nil, err
}

Expand Down Expand Up @@ -111,46 +118,44 @@ func (l *AuthLoginCert) Login(client *api.Client) (*api.Secret, error) {
return nil, err
}

tlsConfig := &api.TLSConfig{
Insecure: false,
}

if v, ok := l.params[consts.FieldCACertFile]; ok {
tlsConfig.CACert = v.(string)
}

if v, ok := l.params[consts.FieldCACertDir]; ok {
tlsConfig.CAPath = v.(string)
config := client.CloneConfig()
tlsConfig := config.TLSConfig()
if tlsConfig == nil {
return nil, fmt.Errorf("clone api.Config's TLSConfig is nil")
}

var clientCertFile string
var clientKeyFile string
if v, ok := l.params[consts.FieldCertFile]; ok {
tlsConfig.ClientCert = v.(string)
clientCertFile = v.(string)
}

if v, ok := l.params[consts.FieldKeyFile]; ok {
tlsConfig.ClientKey = v.(string)
}

if v, ok := l.params[consts.FieldTLSServerName]; ok {
tlsConfig.TLSServerName = v.(string)
clientKeyFile = v.(string)
}

if v, ok := l.params[consts.FieldSkipTLSVerify]; ok {
tlsConfig.Insecure = v.(bool)
tlsConfig.InsecureSkipVerify = v.(bool)
}

config := c.CloneConfig()
if err := config.ConfigureTLS(tlsConfig); err != nil {
clientCert, err := tls.LoadX509KeyPair(clientCertFile, clientKeyFile)
if err != nil {
return nil, err
}

c, err = api.NewClient(config)
if err != nil {
return nil, err
tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
return &clientCert, nil
}

if config.CloneHeaders {
c.SetHeaders(client.Headers())
switch t := config.HttpClient.Transport.(type) {
case *helper.TransportWrapper:
if err := t.SetTLSConfig(tlsConfig); err != nil {
return nil, err
}
case *http.Transport:
t.TLSClientConfig = tlsConfig
default:
return nil, fmt.Errorf("HTTPClient has unsupported Transport type %T", t)
}

params := make(map[string]interface{})
Expand Down
36 changes: 33 additions & 3 deletions internal/provider/auth_cert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ import (
"encoding/json"
"fmt"
"net/http"
"os"
"path"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/vault/api"

"github.com/hashicorp/terraform-provider-vault/internal/consts"
"github.com/hashicorp/terraform-provider-vault/testutil"
)

func TestAuthLoginCert_Init(t *testing.T) {
Expand Down Expand Up @@ -206,13 +209,34 @@ func TestAuthLoginCert_Login(t *testing.T) {
w.Write(m)
}

tempDir := t.TempDir()
vinay-gopalan marked this conversation as resolved.
Show resolved Hide resolved

b, k, err := testutil.GenerateCA()
if err != nil {
t.Fatal(err)
}

certFile := path.Join(tempDir, "cert.crt")
if err := os.WriteFile(certFile, b, 0o400); err != nil {
t.Fatal(err)
}

keyFile := path.Join(tempDir, "cert.key")
if err := os.WriteFile(keyFile, k, 0o400); err != nil {
t.Fatal(err)
}

tests := []authLoginTest{
{
name: "default",
authLogin: &AuthLoginCert{
AuthLoginCommon{
authField: "baz",
params: map[string]interface{}{},
authField: "baz",
params: map[string]interface{}{
consts.FieldCertFile: certFile,
consts.FieldKeyFile: keyFile,
consts.FieldSkipTLSVerify: true,
},
initialized: true,
},
},
Expand All @@ -231,6 +255,7 @@ func TestAuthLoginCert_Login(t *testing.T) {
},
},
},
tls: true,
wantErr: false,
},
{
Expand All @@ -240,7 +265,10 @@ func TestAuthLoginCert_Login(t *testing.T) {
authField: "baz",
mount: "qux",
params: map[string]interface{}{
consts.FieldName: "bob",
consts.FieldName: "bob",
consts.FieldCertFile: certFile,
consts.FieldKeyFile: keyFile,
consts.FieldSkipTLSVerify: true,
},
initialized: true,
},
Expand All @@ -262,6 +290,7 @@ func TestAuthLoginCert_Login(t *testing.T) {
},
},
},
tls: true,
wantErr: false,
},
{
Expand All @@ -276,6 +305,7 @@ func TestAuthLoginCert_Login(t *testing.T) {
},
want: nil,
wantErr: true,
tls: true,
expectErr: authLoginInitCheckError,
},
}
Expand Down
12 changes: 10 additions & 2 deletions internal/provider/auth_jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,11 @@ func TestAuthLoginJWT_LoginPath(t *testing.T) {
func TestAuthLoginJWT_Login(t *testing.T) {
handlerFunc := func(t *testLoginHandler, w http.ResponseWriter, req *http.Request) {
m, err := json.Marshal(
&api.Secret{},
&api.Secret{
Data: map[string]interface{}{
"auth_login": "jwt",
},
},
)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
Expand Down Expand Up @@ -146,7 +150,11 @@ func TestAuthLoginJWT_Login(t *testing.T) {
consts.FieldJWT: "jwt1",
},
},
want: &api.Secret{},
want: &api.Secret{
Data: map[string]interface{}{
"auth_login": "jwt",
},
},
wantErr: false,
},
{
Expand Down
Loading