mirror of
https://github.com/hanzoai/telemetry.git
synced 2026-10-30 20:01:49 +00:00
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description When https://github.com/open-telemetry/opentelemetry-collector/pull/13926 is merged, all unit tests in this repository will be run with `GODEBUG=fips140=only` to help surface any FIPS-140 violations in all the Go modules in this repository. One such unit test that fails in these circumstances is `confmap/provider/internal/configurablehttpprovider.TestFunctionalityDownloadFileHTTPS`. However, the FIPS violation surfaced by this test is from the test code itself (as opposed to from OpenTelemetry Collector core code that the test is exercising), specifically when this call is made: https://github.com/open-telemetry/opentelemetry-collector/blob/e0b4b19597eff5783b52400e5f22d007edae0f00/confmap/provider/internal/configurablehttpprovider/provider_test.go#L81 As such, this is not a FIPS violation we need to worry about when running the OpenTelemetry Collector. To prevent the unit test from failing when it's run with `GODEBUG=fips140=only`, we skip it. <!-- Issue number if applicable --> #### Link to tracking issue Fixes #13998 <!--Describe what testing was performed and which tests were added.--> #### Testing Run the `configurablehttpprovider.TestFunctionalityDownloadFileHTTPS` unit test with `GODEBUG=fips140=only`. ``` $ cd confmap/ $ GODEBUG=fips140=only go test ./provider/internal/configurablehttpprovider/... -test.v -test.run TestFunctionalityDownloadFileHTTPS -count 1 ```
322 lines
9.5 KiB
Go
322 lines
9.5 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package configurablehttpprovider
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"math/big"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"go.opentelemetry.io/collector/confmap"
|
|
"go.opentelemetry.io/collector/confmap/confmaptest"
|
|
"go.opentelemetry.io/collector/internal/testutil"
|
|
)
|
|
|
|
func newConfigurableHTTPProvider(scheme SchemeType, set confmap.ProviderSettings) *provider {
|
|
return New(scheme, set).(*provider)
|
|
}
|
|
|
|
func answerGet(w http.ResponseWriter, _ *http.Request) {
|
|
f, err := os.ReadFile("./testdata/otel-config.yaml")
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
_, innerErr := w.Write([]byte("Cannot find the config file"))
|
|
if innerErr != nil {
|
|
fmt.Println("Write failed: ", innerErr)
|
|
}
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
_, err = w.Write(f)
|
|
if err != nil {
|
|
fmt.Println("Write failed: ", err)
|
|
}
|
|
}
|
|
|
|
// Generate a self signed certificate specific for the tests. Based on
|
|
// https://go.dev/src/crypto/tls/generate_cert.go
|
|
func generateCertificate(t *testing.T, hostname string) (cert, key string, err error) {
|
|
testutil.SkipIfFIPSOnly(t, "x509.CreateCertificate uses SHA-1")
|
|
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("Failed to generate private key: %w", err)
|
|
}
|
|
|
|
keyUsage := x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign
|
|
notBefore := time.Now()
|
|
notAfter := notBefore.Add(time.Hour * 12)
|
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("Failed to generate serial number: %w", err)
|
|
}
|
|
|
|
template := x509.Certificate{
|
|
SerialNumber: serialNumber,
|
|
Subject: pkix.Name{
|
|
Organization: []string{"Httpprovider Co"},
|
|
},
|
|
NotBefore: notBefore,
|
|
NotAfter: notAfter,
|
|
KeyUsage: keyUsage,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
|
BasicConstraintsValid: true,
|
|
IsCA: true,
|
|
DNSNames: []string{hostname},
|
|
}
|
|
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("Failed to create certificate: %w", err)
|
|
}
|
|
|
|
tempDir := t.TempDir()
|
|
certOut, err := os.CreateTemp(tempDir, "cert*.pem")
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("Failed to open cert.pem for writing: %w", err)
|
|
}
|
|
|
|
defer certOut.Close()
|
|
|
|
if err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
|
|
return "", "", fmt.Errorf("Failed to write data to cert.pem: %w", err)
|
|
}
|
|
|
|
keyOut, err := os.CreateTemp(tempDir, "key*.pem")
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("Failed to open key.pem for writing: %w", err)
|
|
}
|
|
defer keyOut.Close()
|
|
|
|
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("Unable to marshal private key: %w", err)
|
|
}
|
|
|
|
if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil {
|
|
return "", "", fmt.Errorf("Failed to write data to key.pem: %w", err)
|
|
}
|
|
|
|
return certOut.Name(), keyOut.Name(), nil
|
|
}
|
|
|
|
func TestFunctionalityDownloadFileHTTP(t *testing.T) {
|
|
fp := newConfigurableHTTPProvider(HTTPScheme, confmaptest.NewNopProviderSettings())
|
|
ts := httptest.NewServer(http.HandlerFunc(answerGet))
|
|
defer ts.Close()
|
|
_, err := fp.Retrieve(context.Background(), ts.URL, nil)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, fp.Shutdown(context.Background()))
|
|
}
|
|
|
|
func TestFunctionalityDownloadFileHTTPS(t *testing.T) {
|
|
certPath, keyPath, err := generateCertificate(t, "localhost")
|
|
require.NoError(t, err)
|
|
|
|
invalidCert, err := os.CreateTemp(t.TempDir(), "cert*.crt")
|
|
defer func() { require.NoError(t, invalidCert.Close()) }()
|
|
require.NoError(t, err)
|
|
_, err = invalidCert.Write([]byte{0, 1, 2})
|
|
require.NoError(t, err)
|
|
|
|
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
|
require.NoError(t, err)
|
|
ts := httptest.NewUnstartedServer(http.HandlerFunc(answerGet))
|
|
ts.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
|
|
ts.StartTLS()
|
|
|
|
defer ts.Close()
|
|
|
|
tests := []struct {
|
|
name string
|
|
certPath string
|
|
hostName string
|
|
useCertificate bool
|
|
skipHostnameValidation bool
|
|
shouldError bool
|
|
}{
|
|
{
|
|
name: "Test valid certificate and name",
|
|
certPath: certPath,
|
|
hostName: "localhost",
|
|
useCertificate: true,
|
|
skipHostnameValidation: false,
|
|
shouldError: false,
|
|
},
|
|
{
|
|
name: "Test valid certificate with invalid name",
|
|
certPath: certPath,
|
|
hostName: "127.0.0.1",
|
|
useCertificate: true,
|
|
skipHostnameValidation: false,
|
|
shouldError: true,
|
|
},
|
|
{
|
|
name: "Test valid certificate with invalid name, skip validation",
|
|
certPath: certPath,
|
|
hostName: "127.0.0.1",
|
|
useCertificate: true,
|
|
skipHostnameValidation: true,
|
|
shouldError: false,
|
|
},
|
|
{
|
|
name: "Test no certificate should fail",
|
|
certPath: certPath,
|
|
hostName: "localhost",
|
|
useCertificate: false,
|
|
skipHostnameValidation: false,
|
|
shouldError: true,
|
|
},
|
|
{
|
|
name: "Test invalid cert",
|
|
certPath: invalidCert.Name(),
|
|
hostName: "localhost",
|
|
useCertificate: true,
|
|
skipHostnameValidation: false,
|
|
shouldError: true,
|
|
},
|
|
{
|
|
name: "Test no cert",
|
|
certPath: "no_certificate",
|
|
hostName: "localhost",
|
|
useCertificate: true,
|
|
skipHostnameValidation: false,
|
|
shouldError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
fp := newConfigurableHTTPProvider(HTTPSScheme, confmaptest.NewNopProviderSettings())
|
|
// Parse url of the test server to get the port number.
|
|
tsURL, err := url.Parse(ts.URL)
|
|
require.NoError(t, err)
|
|
if tt.useCertificate {
|
|
fp.caCertPath = tt.certPath
|
|
}
|
|
fp.insecureSkipVerify = tt.skipHostnameValidation
|
|
_, err = fp.Retrieve(context.Background(), fmt.Sprintf("https://%s:%s", tt.hostName, tsURL.Port()), nil)
|
|
if tt.shouldError {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUnsupportedScheme(t *testing.T) {
|
|
fp := New(HTTPScheme, confmaptest.NewNopProviderSettings())
|
|
_, err := fp.Retrieve(context.Background(), "https://...", nil)
|
|
require.Error(t, err)
|
|
require.NoError(t, fp.Shutdown(context.Background()))
|
|
|
|
fp = New(HTTPSScheme, confmaptest.NewNopProviderSettings())
|
|
_, err = fp.Retrieve(context.Background(), "http://...", nil)
|
|
require.Error(t, err)
|
|
assert.NoError(t, fp.Shutdown(context.Background()))
|
|
}
|
|
|
|
func TestEmptyURI(t *testing.T) {
|
|
fp := New(HTTPScheme, confmaptest.NewNopProviderSettings())
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
}))
|
|
defer ts.Close()
|
|
_, err := fp.Retrieve(context.Background(), ts.URL, nil)
|
|
require.Error(t, err)
|
|
require.NoError(t, fp.Shutdown(context.Background()))
|
|
}
|
|
|
|
func TestRetrieveFromShutdownServer(t *testing.T) {
|
|
fp := New(HTTPScheme, confmaptest.NewNopProviderSettings())
|
|
ts := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}))
|
|
ts.Close()
|
|
_, err := fp.Retrieve(context.Background(), ts.URL, nil)
|
|
require.Error(t, err)
|
|
require.NoError(t, fp.Shutdown(context.Background()))
|
|
}
|
|
|
|
func TestNonExistent(t *testing.T) {
|
|
fp := New(HTTPScheme, confmaptest.NewNopProviderSettings())
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
}))
|
|
defer ts.Close()
|
|
_, err := fp.Retrieve(context.Background(), ts.URL, nil)
|
|
require.Error(t, err)
|
|
require.NoError(t, fp.Shutdown(context.Background()))
|
|
}
|
|
|
|
func TestInvalidYAML(t *testing.T) {
|
|
fp := New(HTTPScheme, confmaptest.NewNopProviderSettings())
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
_, err := w.Write([]byte("wrong : ["))
|
|
if err != nil {
|
|
fmt.Println("Write failed: ", err)
|
|
}
|
|
}))
|
|
defer ts.Close()
|
|
ret, err := fp.Retrieve(context.Background(), ts.URL, nil)
|
|
require.NoError(t, err)
|
|
raw, err := ret.AsRaw()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "wrong : [", raw)
|
|
require.NoError(t, fp.Shutdown(context.Background()))
|
|
}
|
|
|
|
func TestScheme(t *testing.T) {
|
|
fp := New(HTTPScheme, confmaptest.NewNopProviderSettings())
|
|
assert.Equal(t, "http", fp.Scheme())
|
|
require.NoError(t, fp.Shutdown(context.Background()))
|
|
}
|
|
|
|
func TestValidateProviderScheme(t *testing.T) {
|
|
assert.NoError(t, confmaptest.ValidateProviderScheme(New(HTTPScheme, confmaptest.NewNopProviderSettings())))
|
|
}
|
|
|
|
func TestInvalidURI(t *testing.T) {
|
|
fp := New(HTTPScheme, confmaptest.NewNopProviderSettings())
|
|
|
|
tests := []struct {
|
|
uri string
|
|
err string
|
|
}{
|
|
{
|
|
uri: "foo://..",
|
|
err: "uri is not supported by \"http\" provider",
|
|
},
|
|
{
|
|
uri: "http://",
|
|
err: "no Host in request URL",
|
|
},
|
|
{
|
|
uri: "http://{}",
|
|
err: "invalid character \"{\" in host name",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.uri, func(t *testing.T) {
|
|
_, err := fp.Retrieve(context.Background(), tt.uri, nil)
|
|
assert.ErrorContains(t, err, tt.err)
|
|
})
|
|
}
|
|
}
|