Skip to content

Commit fd5c88b

Browse files
committed
add http client and tlsSecretRef
1 parent 9d109d3 commit fd5c88b

File tree

10 files changed

+117
-23
lines changed

10 files changed

+117
-23
lines changed

apis/disposablerequest/v1alpha2/disposablerequest_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ type DisposableRequestParameters struct {
4545
// InsecureSkipTLSVerify, when set to true, skips TLS certificate checks for the HTTP request
4646
InsecureSkipTLSVerify bool `json:"insecureSkipTLSVerify,omitempty"`
4747

48+
// TlsSecretRef expects a reference to an opaque secret containing tls.crt and tls.key or/and ca.crt
49+
TlsSecretRef xpv1.SecretReference `json:"tlsSecretRef,omitempty"`
50+
4851
// ExpectedResponse is a jq filter expression used to evaluate the HTTP response and determine if it matches the expected criteria.
4952
// The expression should return a boolean; if true, the response is considered expected.
5053
// Example: '.Body.job_status == "success"'

apis/disposablerequest/v1alpha2/zz_generated.deepcopy.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apis/request/v1alpha2/request_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ type RequestParameters struct {
4242
// InsecureSkipTLSVerify, when set to true, skips TLS certificate checks for the HTTP request
4343
InsecureSkipTLSVerify bool `json:"insecureSkipTLSVerify,omitempty"`
4444

45+
// TlsSecretRef expects a reference to an opaque secret containing tls.crt and tls.key or/and ca.crt
46+
TlsSecretRef xpv1.SecretReference `json:"tlsSecretRef,omitempty"`
47+
4548
// SecretInjectionConfig specifies the secrets receiving patches for response data.
4649
SecretInjectionConfigs []SecretInjectionConfig `json:"secretInjectionConfigs,omitempty"`
4750
}

apis/request/v1alpha2/zz_generated.deepcopy.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/clients/http/client.go

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"bytes"
55
"context"
66
"crypto/tls"
7+
"crypto/x509"
78
"encoding/json"
9+
"errors"
810
"fmt"
911
"io"
1012
"net/http"
@@ -15,12 +17,12 @@ import (
1517

1618
// Client is the interface to interact with Http
1719
type Client interface {
18-
SendRequest(ctx context.Context, method string, url string, body Data, headers Data, skipTLSVerify bool) (resp HttpDetails, err error)
20+
SendRequest(ctx context.Context, method string, url string, body Data, headers Data) (resp HttpDetails, err error)
1921
}
2022

2123
type client struct {
22-
log logging.Logger
23-
timeout time.Duration
24+
client http.Client
25+
log logging.Logger
2426
}
2527

2628
type HttpResponse struct {
@@ -46,7 +48,7 @@ type HttpDetails struct {
4648
HttpRequest HttpRequest
4749
}
4850

49-
func (hc *client) SendRequest(ctx context.Context, method string, url string, body Data, headers Data, skipTLSVerify bool) (details HttpDetails, err error) {
51+
func (hc *client) SendRequest(ctx context.Context, method string, url string, body Data, headers Data) (details HttpDetails, err error) {
5052
requestBody := []byte(body.Decrypted.(string))
5153
request, err := http.NewRequestWithContext(ctx, method, url, bytes.NewBuffer(requestBody))
5254
requestDetails := HttpRequest{
@@ -68,15 +70,7 @@ func (hc *client) SendRequest(ctx context.Context, method string, url string, bo
6870
}
6971
}
7072

71-
client := &http.Client{
72-
Transport: &http.Transport{
73-
// #nosec G402
74-
TLSClientConfig: &tls.Config{InsecureSkipVerify: skipTLSVerify},
75-
},
76-
Timeout: hc.timeout,
77-
}
78-
79-
response, err := client.Do(request)
73+
response, err := hc.client.Do(request)
8074
if err != nil {
8175
return HttpDetails{
8276
HttpRequest: requestDetails,
@@ -112,10 +106,20 @@ func (hc *client) SendRequest(ctx context.Context, method string, url string, bo
112106
}
113107

114108
// NewClient returns a new Http Client
115-
func NewClient(log logging.Logger, timeout time.Duration) (Client, error) {
109+
func NewClient(log logging.Logger, timeout time.Duration, certPEMBlock, keyPEMBlock, caPEMBlock []byte, insecureSkipVerify bool) (Client, error) {
110+
tlsConfig, err := tlsConfig(certPEMBlock, keyPEMBlock, caPEMBlock, insecureSkipVerify)
111+
if err != nil {
112+
return nil, err
113+
}
114+
httpClient := http.Client{
115+
Transport: &http.Transport{
116+
TLSClientConfig: tlsConfig,
117+
},
118+
Timeout: timeout,
119+
}
116120
return &client{
117-
log: log,
118-
timeout: timeout,
121+
client: httpClient,
122+
log: log,
119123
}, nil
120124
}
121125

@@ -127,3 +131,26 @@ func toJSON(request HttpRequest) string {
127131

128132
return string(jsonBytes)
129133
}
134+
135+
func tlsConfig(certPEMBlock, keyPEMBlock, caPEMBlock []byte, insecureSkipVerify bool) (*tls.Config, error) {
136+
tlsConfig := &tls.Config{}
137+
if len(certPEMBlock) > 0 && len(keyPEMBlock) > 0 {
138+
certificate, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
139+
if err != nil {
140+
return nil, err
141+
}
142+
tlsConfig.Certificates = []tls.Certificate{certificate}
143+
}
144+
145+
if len(caPEMBlock) > 0 {
146+
caPool := x509.NewCertPool()
147+
if !caPool.AppendCertsFromPEM(caPEMBlock) {
148+
return nil, errors.New("some error appending the ca.crt")
149+
}
150+
tlsConfig.RootCAs = caPool
151+
}
152+
153+
tlsConfig.InsecureSkipVerify = insecureSkipVerify
154+
155+
return tlsConfig, nil
156+
}

internal/controller/disposablerequest/disposablerequest.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
apisv1alpha1 "github.com/crossplane-contrib/provider-http/apis/v1alpha1"
4343
httpClient "github.com/crossplane-contrib/provider-http/internal/clients/http"
4444
"github.com/crossplane-contrib/provider-http/internal/utils"
45+
corev1 "k8s.io/api/core/v1"
4546
)
4647

4748
const (
@@ -92,7 +93,7 @@ type connector struct {
9293
logger logging.Logger
9394
kube client.Client
9495
usage resource.Tracker
95-
newHttpClientFn func(log logging.Logger, timeout time.Duration) (httpClient.Client, error)
96+
newHttpClientFn func(log logging.Logger, timeout time.Duration, certPEMBlock, keyPEMBlock, caPEMBlock []byte, insecureSkipVerify bool) (httpClient.Client, error)
9697
}
9798

9899
func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) {
@@ -113,7 +114,21 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E
113114
return nil, errors.Wrap(err, errProviderNotRetrieved)
114115
}
115116

116-
h, err := c.newHttpClientFn(l, utils.WaitTimeout(cr.Spec.ForProvider.WaitTimeout))
117+
secret := &corev1.Secret{}
118+
119+
if cr.Spec.ForProvider.TlsSecretRef.Name != "" && cr.Spec.ForProvider.TlsSecretRef.Namespace != "" {
120+
if err := c.kube.Get(ctx, types.NamespacedName{
121+
Namespace: cr.Spec.ForProvider.TlsSecretRef.Namespace,
122+
Name: cr.Spec.ForProvider.TlsSecretRef.Name,
123+
}, secret); err != nil {
124+
return nil, errors.Wrap(err, errGetReferencedSecret)
125+
}
126+
}
127+
certPEMBlock := secret.Data["tls.crt"]
128+
keyPEMBlock := secret.Data["tls.key"]
129+
caPEMBlock := secret.Data["ca.crt"]
130+
131+
h, err := c.newHttpClientFn(l, utils.WaitTimeout(cr.Spec.ForProvider.WaitTimeout), certPEMBlock, keyPEMBlock, caPEMBlock, cr.Spec.ForProvider.InsecureSkipTLSVerify)
117132
if err != nil {
118133
return nil, errors.Wrap(err, errNewHttpClient)
119134
}
@@ -173,7 +188,7 @@ func (c *external) deployAction(ctx context.Context, cr *v1alpha2.DisposableRequ
173188

174189
bodyData := httpClient.Data{Encrypted: cr.Spec.ForProvider.Body, Decrypted: sensitiveBody}
175190
headersData := httpClient.Data{Encrypted: cr.Spec.ForProvider.Headers, Decrypted: sensitiveHeaders}
176-
details, err := c.http.SendRequest(ctx, cr.Spec.ForProvider.Method, cr.Spec.ForProvider.URL, bodyData, headersData, cr.Spec.ForProvider.InsecureSkipTLSVerify)
191+
details, err := c.http.SendRequest(ctx, cr.Spec.ForProvider.Method, cr.Spec.ForProvider.URL, bodyData, headersData)
177192

178193
sensitiveResponse := details.HttpResponse
179194
resource := &utils.RequestResource{

internal/controller/request/observe.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func (c *external) isUpToDate(ctx context.Context, cr *v1alpha2.Request) (Observ
5454
return FailedObserve(), err
5555
}
5656

57-
details, responseErr := c.http.SendRequest(ctx, http.MethodGet, requestDetails.Url, requestDetails.Body, requestDetails.Headers, cr.Spec.ForProvider.InsecureSkipTLSVerify)
57+
details, responseErr := c.http.SendRequest(ctx, http.MethodGet, requestDetails.Url, requestDetails.Body, requestDetails.Headers)
5858
if details.HttpResponse.StatusCode == http.StatusNotFound {
5959
return FailedObserve(), errors.New(errObjectNotFound)
6060
}

internal/controller/request/request.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
"github.com/crossplane-contrib/provider-http/internal/controller/request/statushandler"
4343
datapatcher "github.com/crossplane-contrib/provider-http/internal/data-patcher"
4444
"github.com/crossplane-contrib/provider-http/internal/utils"
45+
corev1 "k8s.io/api/core/v1"
4546
)
4647

4748
const (
@@ -56,6 +57,7 @@ const (
5657
errMappingNotFound = "%s mapping doesn't exist in request, skipping operation"
5758
errPatchDataToSecret = "Warning, couldn't patch data from request to secret %s:%s:%s, error: %s"
5859
errGetLatestVersion = "failed to get the latest version of the resource"
60+
errGetReferencedSecret = "cannot get referenced secret"
5961
)
6062

6163
// Setup adds a controller that reconciles Request managed resources.
@@ -91,7 +93,7 @@ type connector struct {
9193
logger logging.Logger
9294
kube client.Client
9395
usage resource.Tracker
94-
newHttpClientFn func(log logging.Logger, timeout time.Duration) (httpClient.Client, error)
96+
newHttpClientFn func(log logging.Logger, timeout time.Duration, certPEMBlock, keyPEMBlock, caPEMBlock []byte, insecureSkipVerify bool) (httpClient.Client, error)
9597
}
9698

9799
// Connect typically produces an ExternalClient by:
@@ -117,7 +119,21 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E
117119
return nil, errors.Wrap(err, errProviderNotRetrieved)
118120
}
119121

120-
h, err := c.newHttpClientFn(l, utils.WaitTimeout(cr.Spec.ForProvider.WaitTimeout))
122+
secret := &corev1.Secret{}
123+
124+
if cr.Spec.ForProvider.TlsSecretRef.Name != "" && cr.Spec.ForProvider.TlsSecretRef.Namespace != "" {
125+
if err := c.kube.Get(ctx, types.NamespacedName{
126+
Namespace: cr.Spec.ForProvider.TlsSecretRef.Namespace,
127+
Name: cr.Spec.ForProvider.TlsSecretRef.Name,
128+
}, secret); err != nil {
129+
return nil, errors.Wrap(err, errGetReferencedSecret)
130+
}
131+
}
132+
certPEMBlock := secret.Data["tls.crt"]
133+
keyPEMBlock := secret.Data["tls.key"]
134+
caPEMBlock := secret.Data["ca.crt"]
135+
136+
h, err := c.newHttpClientFn(l, utils.WaitTimeout(cr.Spec.ForProvider.WaitTimeout), certPEMBlock, keyPEMBlock, caPEMBlock, cr.Spec.ForProvider.InsecureSkipTLSVerify)
121137
if err != nil {
122138
return nil, errors.Wrap(err, errNewHttpClient)
123139
}
@@ -194,7 +210,7 @@ func (c *external) deployAction(ctx context.Context, cr *v1alpha2.Request, metho
194210
return err
195211
}
196212

197-
details, err := c.http.SendRequest(ctx, mapping.Method, requestDetails.Url, requestDetails.Body, requestDetails.Headers, cr.Spec.ForProvider.InsecureSkipTLSVerify)
213+
details, err := c.http.SendRequest(ctx, mapping.Method, requestDetails.Url, requestDetails.Body, requestDetails.Headers)
198214
c.patchResponseToSecret(ctx, cr, &details.HttpResponse)
199215

200216
statusHandler, err := statushandler.NewStatusHandler(ctx, cr, details, err, c.localKube, c.logger)

package/crds/http.crossplane.io_disposablerequests.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,20 @@ spec:
506506
- secretRef
507507
type: object
508508
type: array
509+
tlsSecretRef:
510+
description: TlsSecretRef expects a reference to an opaque secret
511+
containing tls.crt and tls.key or/and ca.crt
512+
properties:
513+
name:
514+
description: Name of the secret.
515+
type: string
516+
namespace:
517+
description: Namespace of the secret.
518+
type: string
519+
required:
520+
- name
521+
- namespace
522+
type: object
509523
url:
510524
type: string
511525
x-kubernetes-validations:

package/crds/http.crossplane.io_requests.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,20 @@ spec:
540540
- secretRef
541541
type: object
542542
type: array
543+
tlsSecretRef:
544+
description: TlsSecretRef expects a reference to an opaque secret
545+
containing tls.crt and tls.key or/and ca.crt
546+
properties:
547+
name:
548+
description: Name of the secret.
549+
type: string
550+
namespace:
551+
description: Namespace of the secret.
552+
type: string
553+
required:
554+
- name
555+
- namespace
556+
type: object
543557
waitTimeout:
544558
description: WaitTimeout specifies the maximum time duration for
545559
waiting.

0 commit comments

Comments
 (0)