diff --git a/docs/data-sources/http.md b/docs/data-sources/http.md index fc697339..98ba3549 100644 --- a/docs/data-sources/http.md +++ b/docs/data-sources/http.md @@ -42,6 +42,10 @@ The following arguments are supported: * `request_headers` - (Optional) A map of strings representing additional HTTP headers to include in the request. +* `ca_certificate` - (Optional) PEM-encoded root certificates bundle for TLS authentication. + +* `insecure` - (Optional) Whether server should be accessed without verifying the TLS certificate. Defaults to false. + ## Attributes Reference The following attributes are exported: diff --git a/internal/provider/data_source.go b/internal/provider/data_source.go index a1c43ff1..a8fb7820 100644 --- a/internal/provider/data_source.go +++ b/internal/provider/data_source.go @@ -2,6 +2,8 @@ package provider import ( "context" + "crypto/tls" + "crypto/x509" "fmt" "io/ioutil" "mime" @@ -49,6 +51,16 @@ func dataSource() *schema.Resource { Type: schema.TypeString, }, }, + "ca_certificate": { + Type: schema.TypeString, + Optional: true, + }, + + "insecure": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, }, } } @@ -56,8 +68,32 @@ func dataSource() *schema.Resource { func dataSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) { url := d.Get("url").(string) headers := d.Get("request_headers").(map[string]interface{}) + caCert := d.Get("ca_certificate").(string) + + // Get the System Cert Pool + caCertPool, err := x509.SystemCertPool() + if err != nil { + return append(diags, diag.Errorf("Error tls: %s", err)...) + } + + // Use `ca_certificate` cert pool + if caCert != "" { + caCertPool = x509.NewCertPool() + if ok := caCertPool.AppendCertsFromPEM([]byte(caCert)); !ok { + return append(diags, diag.Errorf("Error tls: Can't add the CA certificate to certificate pool")...) + } + } - client := &http.Client{} + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ + RootCAs: caCertPool, + InsecureSkipVerify: d.Get("insecure").(bool), + }, + } + + client := &http.Client{ + Transport: tr, + } req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { diff --git a/internal/provider/data_source_test.go b/internal/provider/data_source_test.go index cdad29ce..33e8197b 100644 --- a/internal/provider/data_source_test.go +++ b/internal/provider/data_source_test.go @@ -1,10 +1,13 @@ package provider import ( + "crypto/x509" + "encoding/pem" "fmt" "net/http" "net/http/httptest" "regexp" + "strings" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -254,42 +257,193 @@ func TestDataSource_utf16(t *testing.T) { // }, // }) // } +const testDataSourceConfig_basic_TLS_insecure = ` +data "http" "http_test" { + url = "%s/meta_%d.txt" + insecure = true +} + +output "body" { + value = data.http.http_test.body +} + +output "response_headers" { + value = data.http.http_test.response_headers +} +` + +func TestDataSource_http200_TLS_insecure(t *testing.T) { + testHttpMock := setUpMockHttpTLSServer() + + defer testHttpMock.server.Close() + + resource.UnitTest(t, resource.TestCase{ + Providers: testProviders, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(testDataSourceConfig_basic_TLS_insecure, testHttpMock.server.URL, 200), + Check: func(s *terraform.State) error { + _, ok := s.RootModule().Resources["data.http.http_test"] + if !ok { + return fmt.Errorf("missing data resource") + } + + outputs := s.RootModule().Outputs + + if outputs["body"].Value != "1.0.0" { + return fmt.Errorf( + `'body' output is %s; want '1.0.0'`, + outputs["body"].Value, + ) + } + + response_headers := outputs["response_headers"].Value.(map[string]interface{}) + + if response_headers["X-Single"].(string) != "foobar" { + return fmt.Errorf( + `'X-Single' response header is %s; want 'foobar'`, + response_headers["X-Single"].(string), + ) + } + + if response_headers["X-Double"].(string) != "1, 2" { + return fmt.Errorf( + `'X-Double' response header is %s; want '1, 2'`, + response_headers["X-Double"].(string), + ) + } + + return nil + }, + }, + }, + }) +} + +const testDataSourceConfig_basic_TLS_CA = ` +data "http" "http_test" { + url = "%s/meta_%d.txt" + ca_certificate = <