1
1
package agent
2
2
3
3
import (
4
+ "errors"
4
5
"fmt"
5
6
"io"
6
7
"net/url"
@@ -10,7 +11,6 @@ import (
10
11
11
12
"github.com/go-logr/logr"
12
13
"github.com/hashicorp/go-multierror"
13
- "github.com/pkg/errors"
14
14
"github.com/spf13/cobra"
15
15
"gopkg.in/yaml.v3"
16
16
"k8s.io/client-go/rest"
@@ -39,7 +39,7 @@ type Config struct {
39
39
// https://preflight.jetstack.io in Jetstack Secure OAuth and Jetstack
40
40
// Secure API Token modes, and https://api.venafi.cloud in Venafi Cloud Key
41
41
// Pair Service Account mode. It is ignored in Venafi Cloud VenafiConnection
42
- // mode.
42
+ // mode and in MachineHub mode .
43
43
Server string `yaml:"server"`
44
44
45
45
// OrganizationID is only used in Jetstack Secure OAuth and Jetstack Secure
@@ -62,6 +62,9 @@ type Config struct {
62
62
ExcludeAnnotationKeysRegex []string `yaml:"exclude-annotation-keys-regex"`
63
63
// Skips label keys that match the given set of regular expressions.
64
64
ExcludeLabelKeysRegex []string `yaml:"exclude-label-keys-regex"`
65
+
66
+ // MachineHub holds config specific to MachineHub mode.
67
+ MachineHub MachineHubConfig `yaml:"machineHub"`
65
68
}
66
69
67
70
type Endpoint struct {
@@ -89,6 +92,33 @@ type VenafiCloudConfig struct {
89
92
UploadPath string `yaml:"upload_path,omitempty"`
90
93
}
91
94
95
+ // MachineHubConfig holds configuration values specific to the CyberArk Machine Hub integration
96
+ type MachineHubConfig struct {
97
+ // Subdomain is the subdomain indicating where data should be pushed. Used
98
+ // for querying the Service Discovery Service to discover the Identity API
99
+ // URL.
100
+ Subdomain string `yaml:"subdomain"`
101
+
102
+ // CredentialsSecretName is the name of a Kubernetes Secret in the same
103
+ // namespace as the agent, which will be watched for a username and password
104
+ // to send to CyberArk Identity for authentication.
105
+ CredentialsSecretName string `yaml:"credentialsSecretName"`
106
+ }
107
+
108
+ func (mhc MachineHubConfig ) Validate () error {
109
+ var errs []error
110
+
111
+ if mhc .Subdomain == "" {
112
+ errs = append (errs , fmt .Errorf ("subdomain must not be empty in MachineHub mode" ))
113
+ }
114
+
115
+ if mhc .CredentialsSecretName == "" {
116
+ errs = append (errs , fmt .Errorf ("credentialsSecretName must not be empty in MachineHub mode" ))
117
+ }
118
+
119
+ return errors .Join (errs ... )
120
+ }
121
+
92
122
type AgentCmdFlags struct {
93
123
// ConfigFilePath (--config-file, -c) is the path to the agent configuration
94
124
// YAML file.
@@ -103,6 +133,9 @@ type AgentCmdFlags struct {
103
133
// --credentials-file.
104
134
VenafiCloudMode bool
105
135
136
+ // MachineHubMode configures the agent to send data to CyberArk Machine Hub.
137
+ MachineHubMode bool
138
+
106
139
// ClientID (--client-id) is the clientID in case of Venafi Cloud Key Pair
107
140
// Service Account mode.
108
141
ClientID string
@@ -303,30 +336,48 @@ func InitAgentCmdFlags(c *cobra.Command, cfg *AgentCmdFlags) {
303
336
)
304
337
c .PersistentFlags ().MarkDeprecated ("disable-compression" , "no longer has an effect" )
305
338
339
+ // This is a hidden feature flag we use to build the "Machine Hub" feature
340
+ // gradually without impacting customers. Once the feature is GA, we will
341
+ // turn this flag "on" by default.
342
+ c .PersistentFlags ().BoolVar (
343
+ & cfg .MachineHubMode ,
344
+ "machine-hub" ,
345
+ false ,
346
+ "Enables the MachineHub mode. The agent will push data to CyberArk MachineHub." ,
347
+ )
348
+ c .PersistentFlags ().MarkHidden ("machine-hub" )
349
+
306
350
}
307
351
308
- type AuthMode string
352
+ // TLSPKMode controls how to authenticate to TLSPK / Jetstack Secure. Only one
353
+ // TLSPKMode may be provided if using using those backends.
354
+ type TLSPKMode string
309
355
310
356
const (
311
- JetstackSecureOAuth AuthMode = "Jetstack Secure OAuth"
312
- JetstackSecureAPIToken AuthMode = "Jetstack Secure API Token"
313
- VenafiCloudKeypair AuthMode = "Venafi Cloud Key Pair Service Account"
314
- VenafiCloudVenafiConnection AuthMode = "Venafi Cloud VenafiConnection"
357
+ JetstackSecureOAuth TLSPKMode = "Jetstack Secure OAuth"
358
+ JetstackSecureAPIToken TLSPKMode = "Jetstack Secure API Token"
359
+ VenafiCloudKeypair TLSPKMode = "Venafi Cloud Key Pair Service Account"
360
+ VenafiCloudVenafiConnection TLSPKMode = "Venafi Cloud VenafiConnection"
361
+
362
+ // It is possible to push to both MachineHub and TLSPK. With this mode, the
363
+ // agent will only push to MachineHub and not to TLSPK.
364
+ Off TLSPKMode = "MachineHub only"
315
365
)
316
366
317
367
// The command-line flags and the config file are combined into this struct by
318
368
// ValidateAndCombineConfig.
319
369
type CombinedConfig struct {
320
- AuthMode AuthMode
321
-
322
- // Used by all modes.
323
- ClusterID string
324
370
DataGatherers []DataGatherer
325
371
Period time.Duration
326
372
BackoffMaxTime time.Duration
373
+ InstallNS string
327
374
StrictMode bool
328
375
OneShot bool
329
- InstallNS string
376
+
377
+ TLSPKMode TLSPKMode
378
+
379
+ // Used by all TLSPK modes.
380
+ ClusterID string
330
381
331
382
// Used by JetstackSecureOAuth, JetstackSecureAPIToken, and
332
383
// VenafiCloudKeypair. Ignored in VenafiCloudVenafiConnection mode.
@@ -351,6 +402,11 @@ type CombinedConfig struct {
351
402
// Only used for testing purposes.
352
403
OutputPath string
353
404
InputPath string
405
+
406
+ // MachineHub-related settings.
407
+ MachineHubMode bool
408
+ MachineHubSubdomain string
409
+ MachineHubCredentialsSecretName string
354
410
}
355
411
356
412
// ValidateAndCombineConfig combines and validates the input configuration with
@@ -366,9 +422,23 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
366
422
res := CombinedConfig {}
367
423
var errs error
368
424
425
+ if flags .MachineHubMode {
426
+ err := cfg .MachineHub .Validate ()
427
+ if err != nil {
428
+ return CombinedConfig {}, nil , fmt .Errorf ("invalid MachineHub config provided: %w" , err )
429
+ }
430
+
431
+ res .MachineHubMode = true
432
+ res .MachineHubSubdomain = cfg .MachineHub .Subdomain
433
+ res .MachineHubCredentialsSecretName = cfg .MachineHub .CredentialsSecretName
434
+
435
+ keysAndValues := []any {"credentialsSecretName" , res .MachineHubCredentialsSecretName }
436
+ log .V (logs .Info ).Info ("Will push to CyberArk MachineHub using a username and password loaded from a Kubernetes Secret" , keysAndValues ... )
437
+ }
438
+
369
439
{
370
440
var (
371
- mode AuthMode
441
+ mode TLSPKMode
372
442
reason string
373
443
keysAndValues []any
374
444
)
@@ -396,19 +466,30 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
396
466
mode = JetstackSecureOAuth
397
467
reason = "--credentials-file was specified without --venafi-cloud"
398
468
default :
399
- return CombinedConfig {}, nil , fmt .Errorf ("no auth mode specified. You can use one of four auth modes:\n " +
400
- " - Use (--venafi-cloud with --credentials-file) or (--client-id with --private-key-path) to use the " + string (VenafiCloudKeypair ) + " mode.\n " +
401
- " - Use --venafi-connection for the " + string (VenafiCloudVenafiConnection ) + " mode.\n " +
402
- " - Use --credentials-file alone if you want to use the " + string (JetstackSecureOAuth ) + " mode.\n " +
403
- " - Use --api-token if you want to use the " + string (JetstackSecureAPIToken ) + " mode.\n " )
469
+ if ! flags .MachineHubMode {
470
+ return CombinedConfig {}, nil , fmt .Errorf ("no TLSPK mode specified and MachineHub mode is disabled. You must either enable the MachineHub mode (using --machine-hub), or enable one of the TLSPK modes.\n " +
471
+ "To enable one of the TLSPK modes, you can:\n " +
472
+ " - Use (--venafi-cloud with --credentials-file) or (--client-id with --private-key-path) to use the " + string (VenafiCloudKeypair ) + " mode.\n " +
473
+ " - Use --venafi-connection for the " + string (VenafiCloudVenafiConnection ) + " mode.\n " +
474
+ " - Use --credentials-file alone if you want to use the " + string (JetstackSecureOAuth ) + " mode.\n " +
475
+ " - Use --api-token if you want to use the " + string (JetstackSecureAPIToken ) + " mode.\n " +
476
+ "Note that it is possible to use one of the TLSPK modes along with the MachineHub mode (--machine-hub)." )
477
+ }
478
+
479
+ mode = Off
404
480
}
405
- res . AuthMode = mode
481
+
406
482
keysAndValues = append (keysAndValues , "mode" , mode , "reason" , reason )
407
- log .V (logs .Debug ).Info ("Authentication mode" , keysAndValues ... )
483
+ if mode != Off {
484
+ log .V (logs .Debug ).Info ("Configured to push to Venafi" , keysAndValues ... )
485
+ }
486
+
487
+ res .TLSPKMode = mode
408
488
}
409
489
410
490
// Validation and defaulting of `server` and the deprecated `endpoint.path`.
411
- {
491
+ if res .TLSPKMode != Off {
492
+ // Only relevant if using TLSPK backends
412
493
hasEndpointField := cfg .Endpoint .Host != "" && cfg .Endpoint .Path != ""
413
494
hasServerField := cfg .Server != ""
414
495
var server string
@@ -430,7 +511,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
430
511
endpointPath = cfg .Endpoint .Path
431
512
case ! hasServerField && ! hasEndpointField :
432
513
server = "https://preflight.jetstack.io"
433
- if res .AuthMode == VenafiCloudKeypair {
514
+ if res .TLSPKMode == VenafiCloudKeypair {
434
515
// The VenafiCloudVenafiConnection mode doesn't need a server.
435
516
server = client .VenafiCloudProdURL
436
517
}
@@ -439,7 +520,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
439
520
if urlErr != nil || url .Hostname () == "" {
440
521
errs = multierror .Append (errs , fmt .Errorf ("server %q is not a valid URL" , server ))
441
522
}
442
- if res .AuthMode == VenafiCloudVenafiConnection && server != "" {
523
+ if res .TLSPKMode == VenafiCloudVenafiConnection && server != "" {
443
524
log .Info (fmt .Sprintf ("ignoring the server field specified in the config file. In %s mode, this field is not needed." , VenafiCloudVenafiConnection ))
444
525
server = ""
445
526
}
@@ -451,9 +532,9 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
451
532
{
452
533
var uploadPath string
453
534
switch {
454
- case res .AuthMode == VenafiCloudKeypair :
535
+ case res .TLSPKMode == VenafiCloudKeypair :
455
536
if cfg .VenafiCloud == nil || cfg .VenafiCloud .UploadPath == "" {
456
- errs = multierror .Append (errs , fmt .Errorf ("the venafi-cloud.upload_path field is required when using the %s mode" , res .AuthMode ))
537
+ errs = multierror .Append (errs , fmt .Errorf ("the venafi-cloud.upload_path field is required when using the %s mode" , res .TLSPKMode ))
457
538
break // Skip to the end of the switch statement.
458
539
}
459
540
_ , urlErr := url .Parse (cfg .VenafiCloud .UploadPath )
@@ -463,14 +544,14 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
463
544
}
464
545
465
546
uploadPath = cfg .VenafiCloud .UploadPath
466
- case res .AuthMode == VenafiCloudVenafiConnection :
547
+ case res .TLSPKMode == VenafiCloudVenafiConnection :
467
548
// The venafi-cloud.upload_path was initially meant to let users
468
549
// configure HTTP proxies, but it has never been used since HTTP
469
550
// proxies don't rewrite paths. Thus, we've disabled the ability to
470
551
// change this value with the new --venafi-connection flag, and this
471
552
// field is simply ignored.
472
553
if cfg .VenafiCloud != nil && cfg .VenafiCloud .UploadPath != "" {
473
- log .Info (fmt .Sprintf (`ignoring the venafi-cloud.upload_path field in the config file. In %s mode, this field is not needed.` , res .AuthMode ))
554
+ log .Info (fmt .Sprintf (`ignoring the venafi-cloud.upload_path field in the config file. In %s mode, this field is not needed.` , res .TLSPKMode ))
474
555
}
475
556
uploadPath = ""
476
557
}
@@ -488,26 +569,26 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
488
569
// https://venafi.atlassian.net/browse/VC-35385 is done.
489
570
{
490
571
if cfg .VenafiCloud != nil && cfg .VenafiCloud .UploaderID != "" {
491
- log .Info (fmt .Sprintf (`ignoring the venafi-cloud.uploader_id field in the config file. This field is not needed in %s mode.` , res .AuthMode ))
572
+ log .Info (fmt .Sprintf (`ignoring the venafi-cloud.uploader_id field in the config file. This field is not needed in %s mode.` , res .TLSPKMode ))
492
573
}
493
574
}
494
575
495
576
// Validation of `cluster_id` and `organization_id`.
496
- {
577
+ if res . TLSPKMode != Off {
497
578
var clusterID string
498
579
var organizationID string // Only used by the old jetstack-secure mode.
499
580
switch {
500
- case res .AuthMode == VenafiCloudKeypair :
581
+ case res .TLSPKMode == VenafiCloudKeypair :
501
582
if cfg .ClusterID == "" {
502
- errs = multierror .Append (errs , fmt .Errorf ("cluster_id is required in %s mode" , res .AuthMode ))
583
+ errs = multierror .Append (errs , fmt .Errorf ("cluster_id is required in %s mode" , res .TLSPKMode ))
503
584
}
504
585
clusterID = cfg .ClusterID
505
- case res .AuthMode == VenafiCloudVenafiConnection :
586
+ case res .TLSPKMode == VenafiCloudVenafiConnection :
506
587
if cfg .ClusterID == "" {
507
- errs = multierror .Append (errs , fmt .Errorf ("cluster_id is required in %s mode" , res .AuthMode ))
588
+ errs = multierror .Append (errs , fmt .Errorf ("cluster_id is required in %s mode" , res .TLSPKMode ))
508
589
}
509
590
clusterID = cfg .ClusterID
510
- case res .AuthMode == JetstackSecureOAuth || res .AuthMode == JetstackSecureAPIToken :
591
+ case res .TLSPKMode == JetstackSecureOAuth || res .TLSPKMode == JetstackSecureAPIToken :
511
592
if cfg .OrganizationID == "" {
512
593
errs = multierror .Append (errs , fmt .Errorf ("organization_id is required" ))
513
594
}
@@ -567,7 +648,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
567
648
res .InstallNS = installNS
568
649
569
650
// Validation of --venafi-connection and --venafi-connection-namespace.
570
- if res .AuthMode == VenafiCloudVenafiConnection {
651
+ if res .TLSPKMode == VenafiCloudVenafiConnection {
571
652
res .VenConnName = flags .VenConnName
572
653
var venConnNS string = flags .VenConnNS
573
654
if flags .VenConnNS == "" {
@@ -634,7 +715,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie
634
715
var preflightClient client.Client
635
716
metadata := & api.AgentMetadata {Version : version .PreflightVersion , ClusterID : cfg .ClusterID }
636
717
switch {
637
- case cfg .AuthMode == JetstackSecureOAuth :
718
+ case cfg .TLSPKMode == JetstackSecureOAuth :
638
719
// Note that there are no command line flags to configure the
639
720
// JetstackSecureOAuth mode.
640
721
credsBytes , err := readCredentialsFile (flagCredentialsPath )
@@ -653,7 +734,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie
653
734
if err != nil {
654
735
errs = multierror .Append (errs , err )
655
736
}
656
- case cfg .AuthMode == VenafiCloudKeypair :
737
+ case cfg .TLSPKMode == VenafiCloudKeypair :
657
738
var creds client.Credentials
658
739
659
740
if flagClientID != "" && flagCredentialsPath != "" {
@@ -696,7 +777,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie
696
777
if err != nil {
697
778
errs = multierror .Append (errs , err )
698
779
}
699
- case cfg .AuthMode == VenafiCloudVenafiConnection :
780
+ case cfg .TLSPKMode == VenafiCloudVenafiConnection :
700
781
var restCfg * rest.Config
701
782
restCfg , err := kubeconfig .LoadRESTConfig ("" )
702
783
if err != nil {
@@ -708,18 +789,20 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie
708
789
if err != nil {
709
790
errs = multierror .Append (errs , err )
710
791
}
711
- case cfg .AuthMode == JetstackSecureAPIToken :
792
+ case cfg .TLSPKMode == JetstackSecureAPIToken :
712
793
var err error
713
794
preflightClient , err = client .NewAPITokenClient (metadata , flagAPIToken , cfg .Server )
714
795
if err != nil {
715
796
errs = multierror .Append (errs , err )
716
797
}
798
+ case cfg .TLSPKMode == Off :
799
+ // No client needed in this mode.
717
800
default :
718
- panic (fmt .Errorf ("programmer mistake: auth mode not implemented: %s" , cfg .AuthMode ))
801
+ panic (fmt .Errorf ("programmer mistake: auth mode not implemented: %s" , cfg .TLSPKMode ))
719
802
}
720
803
721
804
if errs != nil {
722
- return nil , fmt .Errorf ("failed loading config using the %s mode: %w" , cfg .AuthMode , errs )
805
+ return nil , fmt .Errorf ("failed loading config using the %s mode: %w" , cfg .TLSPKMode , errs )
723
806
}
724
807
725
808
return preflightClient , nil
@@ -756,7 +839,7 @@ func createCredentialClient(log logr.Logger, credentials client.Credentials, cfg
756
839
uploaderID := "no"
757
840
758
841
var uploadPath string
759
- if cfg .AuthMode == VenafiCloudKeypair {
842
+ if cfg .TLSPKMode == VenafiCloudKeypair {
760
843
// We don't do this for the VenafiCloudVenafiConnection mode because
761
844
// the upload_path field is ignored in that mode.
762
845
log .Info ("Loading upload_path from \" venafi-cloud\" configuration." )
@@ -846,7 +929,7 @@ func (c *Config) Dump() (string, error) {
846
929
d , err := yaml .Marshal (& c )
847
930
848
931
if err != nil {
849
- return "" , errors . Wrap ( err , "failed to generate YAML dump of config" )
932
+ return "" , fmt . Errorf ( "failed to generate YAML dump of config: %w" , err )
850
933
}
851
934
852
935
return string (d ), nil
0 commit comments