Skip to content

Commit 129b53d

Browse files
SgtCoDFishmaelvls
authored andcommitted
machinehub: add the hidden --machine-hub flag
Co-authored-by: Ashley Davis <[email protected]>
1 parent 3a10eeb commit 129b53d

File tree

3 files changed

+207
-66
lines changed

3 files changed

+207
-66
lines changed

pkg/agent/config.go

Lines changed: 126 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package agent
22

33
import (
4+
"errors"
45
"fmt"
56
"io"
67
"net/url"
@@ -10,7 +11,6 @@ import (
1011

1112
"github.com/go-logr/logr"
1213
"github.com/hashicorp/go-multierror"
13-
"github.com/pkg/errors"
1414
"github.com/spf13/cobra"
1515
"gopkg.in/yaml.v3"
1616
"k8s.io/client-go/rest"
@@ -39,7 +39,7 @@ type Config struct {
3939
// https://preflight.jetstack.io in Jetstack Secure OAuth and Jetstack
4040
// Secure API Token modes, and https://api.venafi.cloud in Venafi Cloud Key
4141
// Pair Service Account mode. It is ignored in Venafi Cloud VenafiConnection
42-
// mode.
42+
// mode and in MachineHub mode.
4343
Server string `yaml:"server"`
4444

4545
// OrganizationID is only used in Jetstack Secure OAuth and Jetstack Secure
@@ -62,6 +62,9 @@ type Config struct {
6262
ExcludeAnnotationKeysRegex []string `yaml:"exclude-annotation-keys-regex"`
6363
// Skips label keys that match the given set of regular expressions.
6464
ExcludeLabelKeysRegex []string `yaml:"exclude-label-keys-regex"`
65+
66+
// MachineHub holds config specific to MachineHub mode.
67+
MachineHub MachineHubConfig `yaml:"machineHub"`
6568
}
6669

6770
type Endpoint struct {
@@ -89,6 +92,33 @@ type VenafiCloudConfig struct {
8992
UploadPath string `yaml:"upload_path,omitempty"`
9093
}
9194

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+
92122
type AgentCmdFlags struct {
93123
// ConfigFilePath (--config-file, -c) is the path to the agent configuration
94124
// YAML file.
@@ -103,6 +133,9 @@ type AgentCmdFlags struct {
103133
// --credentials-file.
104134
VenafiCloudMode bool
105135

136+
// MachineHubMode configures the agent to send data to CyberArk Machine Hub.
137+
MachineHubMode bool
138+
106139
// ClientID (--client-id) is the clientID in case of Venafi Cloud Key Pair
107140
// Service Account mode.
108141
ClientID string
@@ -303,30 +336,48 @@ func InitAgentCmdFlags(c *cobra.Command, cfg *AgentCmdFlags) {
303336
)
304337
c.PersistentFlags().MarkDeprecated("disable-compression", "no longer has an effect")
305338

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+
306350
}
307351

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
309355

310356
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"
315365
)
316366

317367
// The command-line flags and the config file are combined into this struct by
318368
// ValidateAndCombineConfig.
319369
type CombinedConfig struct {
320-
AuthMode AuthMode
321-
322-
// Used by all modes.
323-
ClusterID string
324370
DataGatherers []DataGatherer
325371
Period time.Duration
326372
BackoffMaxTime time.Duration
373+
InstallNS string
327374
StrictMode bool
328375
OneShot bool
329-
InstallNS string
376+
377+
TLSPKMode TLSPKMode
378+
379+
// Used by all TLSPK modes.
380+
ClusterID string
330381

331382
// Used by JetstackSecureOAuth, JetstackSecureAPIToken, and
332383
// VenafiCloudKeypair. Ignored in VenafiCloudVenafiConnection mode.
@@ -351,6 +402,11 @@ type CombinedConfig struct {
351402
// Only used for testing purposes.
352403
OutputPath string
353404
InputPath string
405+
406+
// MachineHub-related settings.
407+
MachineHubMode bool
408+
MachineHubSubdomain string
409+
MachineHubCredentialsSecretName string
354410
}
355411

356412
// ValidateAndCombineConfig combines and validates the input configuration with
@@ -366,9 +422,23 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
366422
res := CombinedConfig{}
367423
var errs error
368424

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+
369439
{
370440
var (
371-
mode AuthMode
441+
mode TLSPKMode
372442
reason string
373443
keysAndValues []any
374444
)
@@ -396,19 +466,30 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
396466
mode = JetstackSecureOAuth
397467
reason = "--credentials-file was specified without --venafi-cloud"
398468
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
404480
}
405-
res.AuthMode = mode
481+
406482
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
408488
}
409489

410490
// Validation and defaulting of `server` and the deprecated `endpoint.path`.
411-
{
491+
if res.TLSPKMode != Off {
492+
// Only relevant if using TLSPK backends
412493
hasEndpointField := cfg.Endpoint.Host != "" && cfg.Endpoint.Path != ""
413494
hasServerField := cfg.Server != ""
414495
var server string
@@ -430,7 +511,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
430511
endpointPath = cfg.Endpoint.Path
431512
case !hasServerField && !hasEndpointField:
432513
server = "https://preflight.jetstack.io"
433-
if res.AuthMode == VenafiCloudKeypair {
514+
if res.TLSPKMode == VenafiCloudKeypair {
434515
// The VenafiCloudVenafiConnection mode doesn't need a server.
435516
server = client.VenafiCloudProdURL
436517
}
@@ -439,7 +520,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
439520
if urlErr != nil || url.Hostname() == "" {
440521
errs = multierror.Append(errs, fmt.Errorf("server %q is not a valid URL", server))
441522
}
442-
if res.AuthMode == VenafiCloudVenafiConnection && server != "" {
523+
if res.TLSPKMode == VenafiCloudVenafiConnection && server != "" {
443524
log.Info(fmt.Sprintf("ignoring the server field specified in the config file. In %s mode, this field is not needed.", VenafiCloudVenafiConnection))
444525
server = ""
445526
}
@@ -451,9 +532,9 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
451532
{
452533
var uploadPath string
453534
switch {
454-
case res.AuthMode == VenafiCloudKeypair:
535+
case res.TLSPKMode == VenafiCloudKeypair:
455536
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))
457538
break // Skip to the end of the switch statement.
458539
}
459540
_, urlErr := url.Parse(cfg.VenafiCloud.UploadPath)
@@ -463,14 +544,14 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
463544
}
464545

465546
uploadPath = cfg.VenafiCloud.UploadPath
466-
case res.AuthMode == VenafiCloudVenafiConnection:
547+
case res.TLSPKMode == VenafiCloudVenafiConnection:
467548
// The venafi-cloud.upload_path was initially meant to let users
468549
// configure HTTP proxies, but it has never been used since HTTP
469550
// proxies don't rewrite paths. Thus, we've disabled the ability to
470551
// change this value with the new --venafi-connection flag, and this
471552
// field is simply ignored.
472553
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))
474555
}
475556
uploadPath = ""
476557
}
@@ -488,26 +569,26 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
488569
// https://venafi.atlassian.net/browse/VC-35385 is done.
489570
{
490571
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))
492573
}
493574
}
494575

495576
// Validation of `cluster_id` and `organization_id`.
496-
{
577+
if res.TLSPKMode != Off {
497578
var clusterID string
498579
var organizationID string // Only used by the old jetstack-secure mode.
499580
switch {
500-
case res.AuthMode == VenafiCloudKeypair:
581+
case res.TLSPKMode == VenafiCloudKeypair:
501582
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))
503584
}
504585
clusterID = cfg.ClusterID
505-
case res.AuthMode == VenafiCloudVenafiConnection:
586+
case res.TLSPKMode == VenafiCloudVenafiConnection:
506587
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))
508589
}
509590
clusterID = cfg.ClusterID
510-
case res.AuthMode == JetstackSecureOAuth || res.AuthMode == JetstackSecureAPIToken:
591+
case res.TLSPKMode == JetstackSecureOAuth || res.TLSPKMode == JetstackSecureAPIToken:
511592
if cfg.OrganizationID == "" {
512593
errs = multierror.Append(errs, fmt.Errorf("organization_id is required"))
513594
}
@@ -567,7 +648,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
567648
res.InstallNS = installNS
568649

569650
// Validation of --venafi-connection and --venafi-connection-namespace.
570-
if res.AuthMode == VenafiCloudVenafiConnection {
651+
if res.TLSPKMode == VenafiCloudVenafiConnection {
571652
res.VenConnName = flags.VenConnName
572653
var venConnNS string = flags.VenConnNS
573654
if flags.VenConnNS == "" {
@@ -634,7 +715,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie
634715
var preflightClient client.Client
635716
metadata := &api.AgentMetadata{Version: version.PreflightVersion, ClusterID: cfg.ClusterID}
636717
switch {
637-
case cfg.AuthMode == JetstackSecureOAuth:
718+
case cfg.TLSPKMode == JetstackSecureOAuth:
638719
// Note that there are no command line flags to configure the
639720
// JetstackSecureOAuth mode.
640721
credsBytes, err := readCredentialsFile(flagCredentialsPath)
@@ -653,7 +734,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie
653734
if err != nil {
654735
errs = multierror.Append(errs, err)
655736
}
656-
case cfg.AuthMode == VenafiCloudKeypair:
737+
case cfg.TLSPKMode == VenafiCloudKeypair:
657738
var creds client.Credentials
658739

659740
if flagClientID != "" && flagCredentialsPath != "" {
@@ -696,7 +777,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie
696777
if err != nil {
697778
errs = multierror.Append(errs, err)
698779
}
699-
case cfg.AuthMode == VenafiCloudVenafiConnection:
780+
case cfg.TLSPKMode == VenafiCloudVenafiConnection:
700781
var restCfg *rest.Config
701782
restCfg, err := kubeconfig.LoadRESTConfig("")
702783
if err != nil {
@@ -708,18 +789,20 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie
708789
if err != nil {
709790
errs = multierror.Append(errs, err)
710791
}
711-
case cfg.AuthMode == JetstackSecureAPIToken:
792+
case cfg.TLSPKMode == JetstackSecureAPIToken:
712793
var err error
713794
preflightClient, err = client.NewAPITokenClient(metadata, flagAPIToken, cfg.Server)
714795
if err != nil {
715796
errs = multierror.Append(errs, err)
716797
}
798+
case cfg.TLSPKMode == Off:
799+
// No client needed in this mode.
717800
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))
719802
}
720803

721804
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)
723806
}
724807

725808
return preflightClient, nil
@@ -756,7 +839,7 @@ func createCredentialClient(log logr.Logger, credentials client.Credentials, cfg
756839
uploaderID := "no"
757840

758841
var uploadPath string
759-
if cfg.AuthMode == VenafiCloudKeypair {
842+
if cfg.TLSPKMode == VenafiCloudKeypair {
760843
// We don't do this for the VenafiCloudVenafiConnection mode because
761844
// the upload_path field is ignored in that mode.
762845
log.Info("Loading upload_path from \"venafi-cloud\" configuration.")
@@ -846,7 +929,7 @@ func (c *Config) Dump() (string, error) {
846929
d, err := yaml.Marshal(&c)
847930

848931
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)
850933
}
851934

852935
return string(d), nil

0 commit comments

Comments
 (0)