Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
43 changes: 43 additions & 0 deletions internal/cyberark/api/telemetry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package api

import (
"encoding/base64"
"net/http"
"net/url"

"github.com/jetstack/preflight/pkg/version"
)

// Integrations working with the Identity Security Platform, should add metadata
// in their API calls, to provide insights into how customers utilize each API.
//
// - IntegrationName (in): The vendor integration name (required)
// - IntegrationType (it): Integration Type (required)
// - IntegrationVersion (iv): The plugin version being used (required)
// - VendorName (vn): Vendor name (required)
// - VendorVersion (vv): Version of the vendor product in which the plugin is used (if applicable)

const (
// TelemetryHeaderKey is the name of the HTTP header to use for telemetry
TelemetryHeaderKey = "X-Cybr-Telemetry"
)

var (
telemetryValues url.Values
telemetryValueEncoded string
)

func init() {
telemetryValues = url.Values{}
telemetryValues.Set("in", "cyberark-disco-agent")
telemetryValues.Set("vn", "CyberArk")
telemetryValues.Set("it", "KubernetesAgent")
telemetryValues.Set("iv", version.PreflightVersion)
telemetryValueEncoded = base64.URLEncoding.EncodeToString([]byte(telemetryValues.Encode()))
}

// SetTelemetryRequestHeader adds the x-cybr-telemetry header to the given HTTP
// request, with information about this integration.
func SetTelemetryRequestHeader(req *http.Request) {
req.Header.Set(TelemetryHeaderKey, telemetryValueEncoded)
}
31 changes: 31 additions & 0 deletions internal/cyberark/api/telemetry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package api

import (
"encoding/base64"
"net/http"
"net/url"
"testing"

"github.com/stretchr/testify/require"
)

// Test the SetTelemetryRequestHeader function
func TestSetTelemetryRequestHeader(t *testing.T) {
// Create a new HTTP request
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "http://example.com", nil)
require.NoError(t, err, "failed to create HTTP request")

// Call the function to set the telemetry header
SetTelemetryRequestHeader(req)

base64Value := req.Header.Get(TelemetryHeaderKey)
// Check that the header is set
require.NotEmpty(t, base64Value, "telemetry header should be set")

queryString, err := base64.URLEncoding.DecodeString(base64Value)
require.NoError(t, err, "failed to decode telemetry header value")

values, err := url.ParseQuery(string(queryString))
require.NoError(t, err, "failed to parse telemetry header value")
require.Equal(t, telemetryValues, values, "telemetry header value should match expected values")
}
4 changes: 4 additions & 0 deletions internal/cyberark/dataupload/dataupload.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"k8s.io/apimachinery/pkg/runtime"

arkapi "github.com/jetstack/preflight/internal/cyberark/api"
"github.com/jetstack/preflight/pkg/version"
)

Expand Down Expand Up @@ -168,6 +169,9 @@ func (c *CyberArkClient) retrievePresignedUploadURL(ctx context.Context, checksu
}
version.SetUserAgent(req)

// Add telemetry headers
arkapi.SetTelemetryRequestHeader(req)

res, err := c.httpClient.Do(req)
if err != nil {
return "", err
Expand Down
13 changes: 13 additions & 0 deletions internal/cyberark/dataupload/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/require"
"k8s.io/client-go/transport"

arkapi "github.com/jetstack/preflight/internal/cyberark/api"
"github.com/jetstack/preflight/pkg/version"
)

Expand Down Expand Up @@ -82,6 +83,12 @@ func (mds *mockDataUploadServer) handleSnapshotLinks(w http.ResponseWriter, r *h
return
}

if r.Header.Get(arkapi.TelemetryHeaderKey) == "" {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte("should set telemetry header on all requests"))
return
}

if r.Header.Get("Content-Type") != "application/json" {
http.Error(w, "should send JSON on all requests", http.StatusInternalServerError)
return
Expand Down Expand Up @@ -159,6 +166,12 @@ func (mds *mockDataUploadServer) handlePresignedUpload(w http.ResponseWriter, r
return
}

if r.Header.Get(arkapi.TelemetryHeaderKey) != "" {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte("should NOT set telemetry header on requests to presigned URL"))
return
}

amzChecksum := r.Header.Get("X-Amz-Checksum-Sha256")
if amzChecksum == "" {
http.Error(w, "should set x-amz-checksum-sha256 header on all requests", http.StatusInternalServerError)
Expand Down
3 changes: 3 additions & 0 deletions internal/cyberark/identity/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"k8s.io/klog/v2"

arkapi "github.com/jetstack/preflight/internal/cyberark/api"
"github.com/jetstack/preflight/pkg/logs"
"github.com/jetstack/preflight/pkg/version"
)
Expand Down Expand Up @@ -422,4 +423,6 @@ func setIdentityHeaders(r *http.Request) {
r.Header.Set("Content-Type", "application/json")
r.Header.Set("X-IDAP-NATIVE-CLIENT", "true") //nolint: canonicalheader
version.SetUserAgent(r)
// Add telemetry headers
arkapi.SetTelemetryRequestHeader(r)
}
9 changes: 7 additions & 2 deletions internal/cyberark/identity/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"
"k8s.io/client-go/transport"

arkapi "github.com/jetstack/preflight/internal/cyberark/api"
"github.com/jetstack/preflight/pkg/version"

_ "embed"
Expand Down Expand Up @@ -108,6 +110,10 @@ func checkRequestHeaders(r *http.Request) error {
errs = append(errs, fmt.Errorf("should set X-IDAP-NATIVE-CLIENT header to true on all requests"))
}

if r.Header.Get(arkapi.TelemetryHeaderKey) == "" {
errs = append(errs, fmt.Errorf("should set telemetry header on all requests"))
}

return errors.Join(errs...)
}

Expand All @@ -120,9 +126,8 @@ func (mis *mockIdentityServer) handleStartAuthentication(w http.ResponseWriter,
return
}

if err := checkRequestHeaders(r); err != nil {
if err := checkRequestHeaders(r); !assert.NoError(mis.t, err, "request headers are not correct") {
w.WriteHeader(http.StatusForbidden)
fmt.Fprintf(w, `{"message":"issues with headers sent to mock server: %s"}`, err.Error())
return
}

Expand Down
4 changes: 3 additions & 1 deletion internal/cyberark/servicediscovery/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"os"
"path"

arkapi "github.com/jetstack/preflight/internal/cyberark/api"
"github.com/jetstack/preflight/pkg/version"
)

Expand Down Expand Up @@ -109,7 +110,8 @@ func (c *Client) DiscoverServices(ctx context.Context, subdomain string) (*Servi

request.Header.Set("Accept", "application/json")
version.SetUserAgent(request)

// Add telemetry headers
arkapi.SetTelemetryRequestHeader(request)
resp, err := c.client.Do(request)
if err != nil {
return nil, fmt.Errorf("failed to perform HTTP request: %s", err)
Expand Down
7 changes: 7 additions & 0 deletions internal/cyberark/servicediscovery/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"k8s.io/client-go/transport"

arkapi "github.com/jetstack/preflight/internal/cyberark/api"
"github.com/jetstack/preflight/pkg/version"

_ "embed"
Expand Down Expand Up @@ -92,6 +93,12 @@ func (mds *mockDiscoveryServer) ServeHTTP(w http.ResponseWriter, r *http.Request
return
}

if r.Header.Get(arkapi.TelemetryHeaderKey) == "" {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte("should set telemetry header on all requests"))
return
}

if r.Header.Get("Accept") != "application/json" {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte("should request JSON on all requests"))
Expand Down