Skip to content

[VC-43753] CyberArk Discovery and Context: Upload data in the JSON format required by the API #684

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

Open
wants to merge 17 commits into
base: master
Choose a base branch
from

Conversation

wallrj-cyberark
Copy link
Contributor

@wallrj-cyberark wallrj-cyberark commented Aug 8, 2025

Fixes: https://venafi.atlassian.net/browse/VC-43753

  • Updates the dataupload client to send data in the Snapshot format which is required by the Discovery and Context API.
  • Fixes some bugs and omissions in the Mock API server tests.

The client still deviates from the API requirements, as follows:

  • "roles" : currently all roles and cluster roles are uploaded. The API spec says that only roles relevant for secrets should be uploaded; which means only roles are having any permissions on the secrets
  • "role_bindings": currently all rolebindings and clusterrolebindings are uploaded. The API spec says only role bindings related to the above roles should be uploaded.

...but I prefer to address those in followup PRs, as time permits.

Testing

I ran the Cyberark dataupload client Integration test manually (it's not yet automated):

$ go test -v ./pkg/internal/cyberark/dataupload/...  -run RealAPI -args -testing.v 6
warning: both GOPATH and GOROOT are the same directory (/home/richard/go); see https://go.dev/wiki/InstallTroubleshooting
=== RUN   TestCyberArkClient_PostDataReadingsWithOptions_RealAPI
    round_trippers.go:632: I0809 14:53:42.163896] Response verb="GET" url="https://platform-discovery.integration-cyberark.cloud/api/v2/services/subdomain/tlskp-test" status="200 OK" milliseconds=258
    round_trippers.go:632: I0809 14:53:42.555901] Response verb="POST" url="https://anb5751.id.integration-cyberark.cloud/Security/StartAuthentication" status="200 OK" milliseconds=391
    identity.go:330: I0809 14:53:42.556632] made successful request to StartAuthentication source="Identity.doStartAuthentication" summary="NewPackage"
    round_trippers.go:632: I0809 14:53:42.940690] Response verb="POST" url="https://anb5751.id.integration-cyberark.cloud/Security/AdvanceAuthentication" status="200 OK" milliseconds=383
    identity.go:446: I0809 14:53:42.940928] successfully completed AdvanceAuthentication request to CyberArk Identity; login complete username="[email protected]"
    round_trippers.go:632: I0809 14:53:43.544033] Response verb="POST" url="https://tlskp-test.inventory.integration-cyberark.cloud/api/ingestions/kubernetes/snapshot-links" status="200 OK" milliseconds=588
    round_trippers.go:632: I0809 14:53:44.547562] Response verb="PUT" url="https://kubernetes-snapshots-marshmallow-a1b2c3d4-integration.s3.amazonaws.com/8f08a102-58ca-49cd-960e-debc5e0d3cd4/bb068932-c80d-460d-88df-34bc7f3f3297/20250809_135343_486.snapshot?AWSAccessKeyId=ASIAR53UX6ITHAZ3OMHA&Signature=Aq9CeB%2BOOmY2As%2FtMm5a9FI3oP8%3D&x-amz-tagging=agent_version%3Ddevelopment%26uploader_id%3Dbb068932-c80d-460d-88df-34bc7f3f3297%26vendor%3Dk8s%26checksum_sha256%3D1a4c7ef47ed49fbd14ab3268e84c5a7c9452e5de8a57c84d7bc830a4a93153a7%26tenant_id%3D8f08a102-58ca-49cd-960e-debc5e0d3cd4%26username%3Drichard_wall%40cyberark.cloud.420375&x-amz-security-token=IQoJb3JpZ2luX2VjEIb%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJIMEYCIQDiTFMJclEXEDqXvXMWjdDKbe84UCrbW%2Bb%2BbRxISSLohQIhANb76MH2qQDN7dNOa7NGBrjYoM4B8BHrYey4VCulUXcUKtwDCL%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMMTMyODUxOTU0MjE0Igxdc7ta3upkex7CFSAqsAPNgfX0UNEdHbi2crFsvSSo8swH5pHvJa5sqADbqqP6txOWmMAv077laL4EVxBfOYShUuubYcBy3vkMjBWj5Q2sAXcWINXL0QLRIhJAYroyDBG0ijgvSxIKLxvYFu5mvTBx34ZtSgbVGB626zjCjDZM8gdc8a27UVAHsRJb6EFXr2w6v8733BOTEZrphMrXxhMFFo9c0d83y4g1dZcRMWW3wjvIa5Tuwi5RjOwi5J1Zsn%2BLso%2ByeE9o6MwSi%2F8t6%2FDI9Xtpln68v778O8nGzQFv1JDo8PgLKKYBnNVXOxtebcKPV21Mhp7WpREl1aHnZPoqXS94YqXj0eO4JW0%2BmSSgSCoOBaZ9e51Nt3BVlmRXkKMaEIRBEUmxH3VKBaWY%2F67u0vBW1gQJDlbSX1R0p1MHBDTC4rW5snwP8DiuVdKiNdNGMxe3Lbdz6ShYrq2LVNDREKwPYfyhff3OClC1KIn5xRj0%2BSZ1V8onW2BnBkPGLrorVdshHnGhJw%2B%2FogE4ELXpbd7cGfZh3QBPjhw0uhMV%2FZhhcPmWkNK5snkVLOKYWme9U8PmZRVSNrP8A4zCqFowyqXdxAY6nQFpxwSSeHqWuc146dr8hMPJzpVgAGoHDc4EpPAKuJrgOXv%2FNsixU%2BK8yPGF2Pi%2BkcOzfHzuQb%2BezZyTvpn8HyJ3DyBvWZsm3gHfgX6KinXsyNllzLvE%2F7ko%2BO0dTSiO%2BUlF%2BpNgd%2Fj9wM%2FiKNpCNCHcQrPqRoDcSBPwPq2Gm%2Biic5KoMyHpiZxA8rCpf%2BI6QAVdAKOSpn2GpxovYzfP&Expires=1754747923" status="200 OK" milliseconds=1003
--- PASS: TestCyberArkClient_PostDataReadingsWithOptions_RealAPI (2.64s)
PASS
ok      github.com/jetstack/preflight/pkg/internal/cyberark/dataupload  2.685s

This PR touches some code shared with the TLSPK agent uploads so I ran the e2e test script manually and verified that the test certificate was uploaded and processed by the TLSPK backend:

$ ./hack/e2e/test.sh
...
{
  "ts": 1754748136841.2498,
  "caller": "transport/round_trippers.go:632",
  "msg": "Response",
  "v": 6,
  "logger": "Run.gatherAndOutputData",
  "verb": "POST",
  "url": "https://api.venafi.cloud/v1/tlspk/upload/clusterdata/no?description=QSBraW5kIGNsdXN0ZXIgdXNlZCBmb3IgdGVzdGluZyB0aGUgdmVuYWZpLWt1YmVybmV0ZXMtYWdlbnQuCg&name=venafi-kubernetes-agent-e2e",
  "status": "200 OK",
  "milliseconds": 338
}
{"ts":1754748136906.9675,"caller":"agent/run.go:460","msg":"Data sent successfully","v":0,"logger":"Run.gatherAndOutputData.postData"}
...
{
  "count": 1,
  "certificates": [
    {
      "id": "e1d70740-7529-11f0-a38a-f7545647ce40",
      "companyId": "756db001-280e-11ee-84fb-991f3177e2d0",
      "managedCertificateId": "e2cbc780-7529-11f0-995f-751da7434dbb",
      "fingerprint": "9FF2FE5A78E1E495465F5E4704361BBD8C304926",
      "certificateName": "venafi-kubernetes-agent-e2e.188a1c0f-15ae-4bd4-b4d3-4eaa36eec5a1",
...
}
+ exit 0
image

wallrj added 17 commits August 8, 2025 12:24
Signed-off-by: Richard Wall <[email protected]>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I deleted this file because it was causing import cycle.

$ go test -v ./pkg/datagatherer/...
# github.com/jetstack/preflight/pkg/datagatherer/k8s
package github.com/jetstack/preflight/pkg/datagatherer/k8s
        imports github.com/jetstack/preflight/pkg/testutil from fieldfilter_test.go
        imports github.com/jetstack/preflight/pkg/client from envtest.go
        imports github.com/jetstack/preflight/pkg/internal/cyberark/dataupload from client_cyberark.go
        imports github.com/jetstack/preflight/pkg/datagatherer/k8s from dataupload.go: import cycle not allowed in test
FAIL    github.com/jetstack/preflight/pkg/datagatherer/k8s [setup failed]
?       github.com/jetstack/preflight/pkg/datagatherer  [no test files]
?       github.com/jetstack/preflight/pkg/datagatherer/local    [no test files]
FAIL

Resource interface{}
DeletedAt Time
Resource interface{} `json:"resource"`
DeletedAt Time `json:"deleted_at,omitempty"`
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added json annotations here so that I can unmarshal date readings from a file, for testing.

The agent already has an --input-file option, but stops decoding the input at api.DataReading.Data, leaving the actual data as interface{}.

In the test in this PR I need to decode the Data, so that it has the same types as the DataGatherer.Fetch return values.

type DiscoveryData struct {
ServerVersion *version.Info `json:"server_version"`
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a data type instead of the ad-hoc map that was previously returned by this data gatherer,
to make it easier to do a type conversion in ConverDatareadingsToCyberarkSnapshot function.

@@ -307,14 +307,17 @@ func (g *DataGathererDynamic) WaitForCacheSync(ctx context.Context) error {
return nil
}

type DynamicData struct {
Items []*api.GatheredResource `json:"items"`
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a data type instead of the ad-hoc map that was previously returned by this data gatherer,
to make it easier to do a type conversion in ConverDatareadingsToCyberarkSnapshot function.

@@ -16,6 +16,9 @@ var SecretSelectedFields = []FieldPath{
{"metadata", "ownerReferences"},
{"metadata", "selfLink"},
{"metadata", "uid"},
{"metadata", "creationTimestamp"},
{"metadata", "deletionTimestamp"},
{"metadata", "resourceVersion"},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Cyberark backend needs this extra metadata to produce its reports.
I think it makes sense to upload this metadata for TLSPK too.
I can't see any harm in it and it will be difficult to implement different field filters for the TLSPK vs CyberArk uploaded data.

@@ -41,10 +42,7 @@ func (mds *mockDataUploadServer) ServeHTTP(w http.ResponseWriter, r *http.Reques
mds.handlePresignedUpload(w, r)
return
case "/presigned-upload":
mds.handleUpload(w, r, false)
return
case "/presigned-upload-invalid-json":
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mock endpoint was unused, and was passing exactly the same arguments to handleUpload.
Probably a legacy of a previous version of these tests.
So I deleted it.

_, err := w.Write([]byte(`{"message":"method not allowed"}`))
if err != nil {
panic(err)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some extra error checking here and below. These errors should normally be nil

w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"url":`)) // invalid JSON
return
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This branch wasn't used.
The AWS presigned URL endpoint only returns JSON for errors. Not for StatusOK.

w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"success":true}`))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AWS endpoint returns an empty response body on success.

d.DisallowUnknownFields()
if err := d.Decode(&snapshot); err != nil {
panic(err)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verifies that the new Snapshot format is used in the request body.

@wallrj-cyberark wallrj-cyberark marked this pull request as ready for review August 9, 2025 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants