Skip to content

Commit d26120d

Browse files
committed
WIP: Proof of concept / demo
Signed-off-by: Richard Wall <[email protected]>
1 parent a361867 commit d26120d

File tree

8 files changed

+155
-67
lines changed

8 files changed

+155
-67
lines changed

deploy/charts/venafi-kubernetes-agent/templates/deployment.yaml

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,26 @@ spec:
4949
valueFrom:
5050
fieldRef:
5151
fieldPath: spec.nodeName
52+
- name: ARK_USERNAME
53+
valueFrom:
54+
secretKeyRef:
55+
name: {{ .Values.authentication.secretName }}
56+
key: ARK_USERNAME
57+
- name: ARK_SECRET
58+
valueFrom:
59+
secretKeyRef:
60+
name: {{ .Values.authentication.secretName }}
61+
key: ARK_SECRET
62+
- name: ARK_PLATFORM_DOMAIN
63+
valueFrom:
64+
secretKeyRef:
65+
name: {{ .Values.authentication.secretName }}
66+
key: ARK_PLATFORM_DOMAIN
67+
- name: ARK_SUBDOMAIN
68+
valueFrom:
69+
secretKeyRef:
70+
name: {{ .Values.authentication.secretName }}
71+
key: ARK_SUBDOMAIN
5272
{{- with .Values.http_proxy }}
5373
- name: HTTP_PROXY
5474
value: {{ . }}
@@ -71,18 +91,8 @@ spec:
7191
- "agent"
7292
- "-c"
7393
- "/etc/venafi/agent/config/{{ default "config.yaml" .Values.config.configmap.key }}"
74-
{{- if .Values.authentication.venafiConnection.enabled }}
75-
- --venafi-connection
76-
- {{ .Values.authentication.venafiConnection.name | quote }}
77-
- --venafi-connection-namespace
78-
- {{ .Values.authentication.venafiConnection.namespace | quote }}
79-
{{- else }}
80-
- "--client-id"
81-
- {{ .Values.config.clientId | quote }}
82-
- "--private-key-path"
83-
- "/etc/venafi/agent/key/{{ .Values.authentication.secretKey }}"
84-
{{- end }}
85-
- --venafi-cloud
94+
- --log-level=6
95+
- --machine-hub
8696
{{- if .Values.metrics.enabled }}
8797
- --enable-metrics
8898
{{- end }}
@@ -95,11 +105,6 @@ spec:
95105
- name: config
96106
mountPath: "/etc/venafi/agent/config"
97107
readOnly: true
98-
{{- if not .Values.authentication.venafiConnection.enabled }}
99-
- name: credentials
100-
mountPath: "/etc/venafi/agent/key"
101-
readOnly: true
102-
{{- end }}
103108
{{- with .Values.volumeMounts }}
104109
{{- toYaml . | nindent 12 }}
105110
{{- end }}
@@ -137,12 +142,6 @@ spec:
137142
configMap:
138143
name: {{ default "agent-config" .Values.config.configmap.name }}
139144
optional: false
140-
{{- if not .Values.authentication.venafiConnection.enabled }}
141-
- name: credentials
142-
secret:
143-
secretName: {{ .Values.authentication.secretName }}
144-
optional: false
145-
{{- end }}
146145
{{- with .Values.volumes }}
147146
{{- toYaml . | nindent 8 }}
148147
{{- end }}

hack/e2e/ca/config.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
machineHub:
2+
subdomain: tlskp-test
3+
credentialsSecretName: todo-unused

hack/e2e/ca/test.sh

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/usr/bin/env bash
2+
#
3+
set -o nounset
4+
set -o errexit
5+
set -o pipefail
6+
7+
# CyberArk API configuration
8+
: ${ARK_USERNAME?}
9+
: ${ARK_SECRET?}
10+
: ${ARK_PLATFORM_DOMAIN?}
11+
: ${ARK_SUBDOMAIN?}
12+
13+
# The base URL of the OCI registry used for Docker images and Helm charts
14+
# E.g. ttl.sh/6ee49a01-c8ba-493e-bae9-4d8567574b56
15+
: ${OCI_BASE?}
16+
17+
k8s_namespace=cyberark
18+
19+
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
20+
root_dir=$(cd "${script_dir}/../../.." && pwd)
21+
export TERM=dumb
22+
23+
tmp_dir="$(mktemp -d /tmp/jetstack-secure.XXXXX)"
24+
25+
pushd "${tmp_dir}"
26+
> release.env
27+
make -C "$root_dir" release \
28+
OCI_SIGN_ON_PUSH=false \
29+
oci_platforms=linux/amd64 \
30+
oci_preflight_image_name=$OCI_BASE/images/venafi-agent \
31+
helm_chart_image_name=$OCI_BASE/charts/venafi-kubernetes-agent \
32+
GITHUB_OUTPUT="${tmp_dir}/release.env"
33+
source release.env
34+
35+
kind create cluster || true
36+
kubectl create ns "$k8s_namespace" || true
37+
38+
kubectl create secret generic agent-credentials \
39+
--namespace "$k8s_namespace" \
40+
--from-literal=ARK_USERNAME=$ARK_USERNAME \
41+
--from-literal=ARK_SECRET=$ARK_SECRET \
42+
--from-literal=ARK_PLATFORM_DOMAIN=$ARK_PLATFORM_DOMAIN \
43+
--from-literal=ARK_SUBDOMAIN=$ARK_SUBDOMAIN
44+
45+
helm upgrade agent "oci://${OCI_BASE}/charts/venafi-kubernetes-agent" \
46+
--install \
47+
--create-namespace \
48+
--namespace "$k8s_namespace" \
49+
--version "${RELEASE_HELM_CHART_VERSION}" \
50+
--set fullnameOverride=agent \
51+
--set "image.repository=${OCI_BASE}/images/venafi-agent" \
52+
--values "${script_dir}/values.agent.yaml"
53+
54+
kubectl scale -n "$k8s_namespace" deployment agent --replicas=0
55+
kubectl get cm -n "$k8s_namespace" agent-config -o jsonpath={.data.config\\.yaml} > config.original.yaml
56+
yq eval-all '. as $item ireduce ({}; . * $item)' config.original.yaml "${script_dir}/config.yaml" > config.yaml
57+
kubectl delete cm -n "$k8s_namespace" agent-config
58+
kubectl create cm -n "$k8s_namespace" agent-config --from-file=config.yaml
59+
kubectl scale -n "$k8s_namespace" deployment agent --replicas=1

hack/e2e/ca/values.agent.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Empty

pkg/agent/config.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -606,12 +606,12 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
606606
res.ClusterID = clusterID
607607
res.ClusterDescription = cfg.ClusterDescription
608608

609-
// Validation of `data-gatherers`.
610-
if dgErr := ValidateDataGatherers(cfg.DataGatherers); dgErr != nil {
611-
errs = multierror.Append(errs, dgErr)
612-
}
613-
res.DataGatherers = cfg.DataGatherers
614609
}
610+
// Validation of `data-gatherers`.
611+
if dgErr := ValidateDataGatherers(cfg.DataGatherers); dgErr != nil {
612+
errs = multierror.Append(errs, dgErr)
613+
}
614+
res.DataGatherers = cfg.DataGatherers
615615

616616
// Validation of --period, -p, and the `period` field, as well as
617617
// --backoff-max-time, --one-shot, and --strict. The flag --period/-p takes

pkg/agent/run.go

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ import (
3535
"github.com/jetstack/preflight/pkg/clusteruid"
3636
"github.com/jetstack/preflight/pkg/datagatherer"
3737
"github.com/jetstack/preflight/pkg/datagatherer/k8s"
38+
"github.com/jetstack/preflight/pkg/internal/cyberark/dataupload"
39+
"github.com/jetstack/preflight/pkg/internal/cyberark/identity"
40+
"github.com/jetstack/preflight/pkg/internal/cyberark/servicediscovery"
3841
"github.com/jetstack/preflight/pkg/kubeconfig"
3942
"github.com/jetstack/preflight/pkg/logs"
4043
"github.com/jetstack/preflight/pkg/version"
@@ -79,8 +82,46 @@ func Run(cmd *cobra.Command, args []string) (returnErr error) {
7982
return fmt.Errorf("While evaluating configuration: %v", err)
8083
}
8184

82-
// We need the cluster UID before we progress further so it can be sent along with other data readings
85+
var caClient *dataupload.CyberArkClient
86+
{
87+
platformDomain := os.Getenv("ARK_PLATFORM_DOMAIN")
88+
subdomain := os.Getenv("ARK_SUBDOMAIN")
89+
username := os.Getenv("ARK_USERNAME")
90+
password := []byte(os.Getenv("ARK_SECRET"))
91+
92+
const (
93+
discoveryContextServiceName = "inventory"
94+
separator = "."
95+
)
96+
97+
// TODO(wallrj): Maybe get this URL via the service discovery API.
98+
// https://platform-discovery.integration-cyberark.cloud/api/public/tenant-discovery?allEndpoints=true&bySubdomain=tlskp-test
99+
serviceURL := fmt.Sprintf("https://%s%s%s.%s", subdomain, separator, discoveryContextServiceName, platformDomain)
100+
101+
var (
102+
identityClient *identity.Client
103+
err error
104+
)
105+
if platformDomain == "cyberark.cloud" {
106+
identityClient, err = identity.New(ctx, subdomain)
107+
} else {
108+
discoveryClient := servicediscovery.New(servicediscovery.WithIntegrationEndpoint())
109+
identityClient, err = identity.NewWithDiscoveryClient(ctx, discoveryClient, subdomain)
110+
}
111+
if err != nil {
112+
return fmt.Errorf("while creating the CyberArk identity client: %v", err)
113+
}
114+
if err := identityClient.LoginUsernamePassword(ctx, username, password); err != nil {
115+
return fmt.Errorf("while logging in: %v", err)
116+
}
117+
caClient, err = dataupload.NewCyberArkClient(nil, serviceURL, identityClient.AuthenticateRequest)
118+
if err != nil {
119+
return fmt.Errorf("while creating the CyberArk dataupload client: %v", err)
120+
}
121+
}
83122

123+
// We need the cluster UID before we progress further so it can be sent along with other data readings
124+
// TODO(wallrj): Use the k8s-discovery gatherer to get clusterID
84125
{
85126
restCfg, err := kubeconfig.LoadRESTConfig("")
86127
if err != nil {
@@ -262,7 +303,7 @@ func Run(cmd *cobra.Command, args []string) (returnErr error) {
262303
// be cancelled, which will cause this blocking loop to exit
263304
// instead of waiting for the time period.
264305
for {
265-
if err := gatherAndOutputData(klog.NewContext(ctx, log), eventf, config, preflightClient, dataGatherers); err != nil {
306+
if err := gatherAndOutputData(klog.NewContext(ctx, log), eventf, config, preflightClient, caClient, dataGatherers); err != nil {
266307
return err
267308
}
268309

@@ -316,7 +357,7 @@ func newEventf(log logr.Logger, installNS string) (Eventf, error) {
316357
// Like Printf but for sending events to the agent's Pod object.
317358
type Eventf func(eventType, reason, msg string, args ...interface{})
318359

319-
func gatherAndOutputData(ctx context.Context, eventf Eventf, config CombinedConfig, preflightClient client.Client, dataGatherers map[string]datagatherer.DataGatherer) error {
360+
func gatherAndOutputData(ctx context.Context, eventf Eventf, config CombinedConfig, preflightClient client.Client, caClient *dataupload.CyberArkClient, dataGatherers map[string]datagatherer.DataGatherer) error {
320361
log := klog.FromContext(ctx).WithName("gatherAndOutputData")
321362
var readings []*api.DataReading
322363

@@ -362,8 +403,7 @@ func gatherAndOutputData(ctx context.Context, eventf Eventf, config CombinedConf
362403

363404
if config.MachineHubMode {
364405
post := func() (any, error) {
365-
log.Info("machine hub mode not yet implemented")
366-
return struct{}{}, nil
406+
return struct{}{}, caClient.PostDataReadingsWithOptions(ctx, readings, dataupload.Options{})
367407
}
368408

369409
group.Go(func() error {

pkg/internal/cyberark/dataupload/dataupload.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,13 @@ func extractServerVersionFromReading(reading *api.DataReading) (string, error) {
8080

8181
// ConvertDataReadingsToCyberarkSnapshot converts jetstack-secure DataReadings into Cyberark Snapshot format.
8282
func ConvertDataReadingsToCyberarkSnapshot(
83-
input api.DataReadingsPost,
83+
readings []*api.DataReading,
8484
) (_ *Snapshot, err error) {
8585
k8sVersion := ""
86+
// TODO(wallrj): Use the k8s-discovery gatherer to get clusterID
87+
clusterID := ""
8688
resourceData := ResourceData{}
87-
for _, reading := range input.DataReadings {
89+
for _, reading := range readings {
8890
if reading.DataGatherer == "k8s-discovery" {
8991
k8sVersion, err = extractServerVersionFromReading(reading)
9092
if err != nil {
@@ -102,9 +104,9 @@ func ConvertDataReadingsToCyberarkSnapshot(
102104
}
103105

104106
return &Snapshot{
105-
AgentVersion: input.AgentMetadata.Version,
106-
ClusterID: input.AgentMetadata.ClusterID,
107+
AgentVersion: version.PreflightVersion,
107108
K8SVersion: k8sVersion,
109+
ClusterID: clusterID,
108110
Secrets: resourceData["secrets"],
109111
ServiceAccounts: resourceData["serviceaccounts"],
110112
Roles: resourceData["roles"],
@@ -141,15 +143,17 @@ func NewCyberArkClient(trustedCAs *x509.CertPool, baseURL string, authenticateRe
141143
// PostDataReadingsWithOptions PUTs the supplied payload to an [AWS presigned URL] which it obtains via the CyberArk inventory API.
142144
//
143145
// [AWS presigned URL]: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
144-
func (c *CyberArkClient) PostDataReadingsWithOptions(ctx context.Context, payload api.DataReadingsPost, opts Options) error {
146+
func (c *CyberArkClient) PostDataReadingsWithOptions(ctx context.Context, readings []*api.DataReading, opts Options) error {
145147
if opts.ClusterName == "" {
146148
return fmt.Errorf("programmer mistake: the cluster name (aka `cluster_id` in the config file) cannot be left empty")
147149
}
148150

149-
snapshot, err := ConvertDataReadingsToCyberarkSnapshot(payload)
151+
snapshot, err := ConvertDataReadingsToCyberarkSnapshot(readings)
150152
if err != nil {
151153
return fmt.Errorf("while converting datareadings to Cyberark snapshot format: %s", err)
152154
}
155+
// TODO(wallrj): Use the k8s-discovery gatherer to get clusterID
156+
snapshot.ClusterID = opts.ClusterName
153157

154158
encodedBody := &bytes.Buffer{}
155159
checksum := sha256.New()

pkg/internal/cyberark/dataupload/dataupload_test.go

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,13 @@ import (
3131

3232
func TestCyberArkClient_PostDataReadingsWithOptions_MockAPI(t *testing.T) {
3333
fakeTime := time.Unix(123, 0)
34-
defaultPayload := api.DataReadingsPost{
35-
AgentMetadata: &api.AgentMetadata{
36-
Version: "test-version",
37-
ClusterID: "test",
38-
},
39-
DataGatherTime: fakeTime,
40-
DataReadings: []*api.DataReading{
41-
{
42-
ClusterID: "success-cluster-id",
43-
DataGatherer: "test-gatherer",
44-
Timestamp: api.Time{Time: fakeTime},
45-
Data: map[string]interface{}{"test": "data"},
46-
SchemaVersion: "v1",
47-
},
34+
defaultPayload := []*api.DataReading{
35+
{
36+
ClusterID: "success-cluster-id",
37+
DataGatherer: "test-gatherer",
38+
Timestamp: api.Time{Time: fakeTime},
39+
Data: map[string]interface{}{"test": "data"},
40+
SchemaVersion: "v1",
4841
},
4942
}
5043
defaultOpts := dataupload.Options{
@@ -60,7 +53,7 @@ func TestCyberArkClient_PostDataReadingsWithOptions_MockAPI(t *testing.T) {
6053

6154
tests := []struct {
6255
name string
63-
payload api.DataReadingsPost
56+
payload []*api.DataReading
6457
authenticate func(req *http.Request) error
6558
opts dataupload.Options
6659
requireFn func(t *testing.T, err error)
@@ -197,12 +190,7 @@ func TestCyberArkClient_PostDataReadingsWithOptions_RealAPI(t *testing.T) {
197190
dataReadings := parseDataReadings(t, readGZIP(t, "testdata/example-1/datareadings.json.gz"))
198191
err = cyberArkClient.PostDataReadingsWithOptions(
199192
ctx,
200-
api.DataReadingsPost{
201-
AgentMetadata: &api.AgentMetadata{
202-
ClusterID: "bb068932-c80d-460d-88df-34bc7f3f3297",
203-
},
204-
DataReadings: dataReadings,
205-
},
193+
dataReadings,
206194
dataupload.Options{
207195
ClusterName: "bb068932-c80d-460d-88df-34bc7f3f3297",
208196
},
@@ -272,13 +260,7 @@ func writeGZIP(t *testing.T, path string, data []byte) {
272260

273261
func TestConvertDataReadingsToCyberarkSnapshot(t *testing.T) {
274262
dataReadings := parseDataReadings(t, readGZIP(t, "testdata/example-1/datareadings.json.gz"))
275-
snapshot, err := dataupload.ConvertDataReadingsToCyberarkSnapshot(api.DataReadingsPost{
276-
AgentMetadata: &api.AgentMetadata{
277-
Version: "test-version",
278-
ClusterID: "test-cluster-id",
279-
},
280-
DataReadings: dataReadings,
281-
})
263+
snapshot, err := dataupload.ConvertDataReadingsToCyberarkSnapshot(dataReadings)
282264
require.NoError(t, err)
283265

284266
actualSnapshotBytes, err := json.MarshalIndent(snapshot, "", " ")

0 commit comments

Comments
 (0)