Skip to content

Commit

Permalink
feat: add Agentless support in generate command (#1500)
Browse files Browse the repository at this point in the history
* feat: add Agentless support in generate command

* chore: pr suggestions

* fix: typo
  • Loading branch information
PengyuanZhao authored Jan 10, 2024
1 parent 7288cde commit 4543118
Show file tree
Hide file tree
Showing 9 changed files with 578 additions and 171 deletions.
4 changes: 2 additions & 2 deletions cli/cmd/generate_aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ var (

QuestionAgentlessScanningAccountsReplace = "Currently configured scanning accounts: %s, replace?"
QuestionAgentlessMonitoredAccountIDs = "Monitored AWS account ID list:"
QuestionAgentlessMonitoredAccountIDsHelp = "Please provide a comma seprated list that may " +
QuestionAgentlessMonitoredAccountIDsHelp = "Please provide a comma separated list that may " +
"contain account IDs, OUs, or the organization root (e.g. 123456789000,ou-abcd-12345678,r-abcd)."

QuestionAgentlessMonitoredAccountProfile = "Monitored AWS account profile:"
QuestionAgentlessMonitoredAccountRegion = "Monitored AWS account region:"

// Config questions
QuestionEnableConfig = "Enable configuration integration?"
QuestionEnableConfig = "Enable Configuration integration?"
QuestionConfigAdditionalAccountProfile = "Additional AWS account profile:"
QuestionConfigAdditionalAccountRegion = "Additional AWS account region:"
QuestionConfigAdditionalAccountsReplace = "Currently configured additional accounts: %s, replace?"
Expand Down
94 changes: 79 additions & 15 deletions cli/cmd/generate_gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import (

var (
// Define question text here to be reused in testing
QuestionGcpEnableConfiguration = "Enable configuration integration?"
QuestionGcpEnableAgentless = "Enable Agentless integration?"
QuestionGcpEnableConfiguration = "Enable Configuration integration?"
QuestionGcpEnableAuditLog = "Enable Audit Log integration?"
QuestionUsePubSubAudit = "Use Pub Sub Audit Log?"
QuestionGcpOrganizationIntegration = "Organization integration?"
Expand All @@ -29,6 +30,11 @@ var (
QuestionExistingServiceAccountName = "Specify an existing service account name:"
QuestionExistingServiceAccountPrivateKey = "Specify an existing service account private key (base64 encoded):"

GcpAdvancedOptAgentless = "Configure additional Agentless options"
QuestionGcpProjectFilterList = "Specify a comma separated list of Google Cloud projects that " +
"you want to monitor: (optional)"
QuestionGcpRegions = "Specify a comma separated list of regions to deploy Agentless:"

GcpAdvancedOptAuditLog = "Configure additional Audit Log options"
QuestionGcpUseExistingBucket = "Use an existing bucket?"
QuestionGcpExistingBucketName = "Specify an existing bucket name:"
Expand Down Expand Up @@ -124,6 +130,8 @@ See help output for more details on the parameter value(s) required for Terrafor
gcp.WithWaitTime(GenerateGcpCommandState.WaitTime),
gcp.WithEnableUBLA(GenerateGcpCommandState.EnableUBLA),
gcp.WithMultipleProject(GenerateGcpCommandState.Projects),
gcp.WithProjectFilterList(GenerateGcpCommandState.ProjectFilterList),
gcp.WithRegions(GenerateGcpCommandState.Regions),
}

if GenerateGcpCommandState.OrganizationIntegration {
Expand All @@ -136,6 +144,7 @@ See help output for more details on the parameter value(s) required for Terrafor

// Create new struct
data := gcp.NewTerraform(
GenerateGcpCommandState.Agentless,
GenerateGcpCommandState.Configuration,
GenerateGcpCommandState.AuditLog,
GenerateGcpCommandState.UsePubSubAudit,
Expand Down Expand Up @@ -303,7 +312,12 @@ func initGenerateGcpTfCommandFlags() {
// add flags to sub commands
// TODO Share the help with the interactive generation
generateGcpTfCommand.PersistentFlags().BoolVar(
&GenerateGcpCommandState.AuditLog,
&GenerateGcpCommandState.Agentless,
"agentless",
false,
"enable agentless integration")
generateGcpTfCommand.PersistentFlags().BoolVar(
&GenerateGcpCommandState.Agentless,
"audit_log",
false,
"enable audit log integration")
Expand Down Expand Up @@ -368,6 +382,16 @@ func initGenerateGcpTfCommandFlags() {
"existing_sink_name",
"",
"specify existing sink name")
generateGcpTfCommand.PersistentFlags().StringSliceVar(
&GenerateGcpCommandState.ProjectFilterList,
"project_filter_list",
[]string{},
"List of GCP project IDs to monitor for Agentless integration")
generateGcpTfCommand.PersistentFlags().StringSliceVar(
&GenerateGcpCommandState.Regions,
"regions",
[]string{},
"List of GCP regions to deploy for Agentless integration")

// DEPRECATED
generateGcpTfCommand.PersistentFlags().BoolVar(
Expand Down Expand Up @@ -485,6 +509,26 @@ func validateGcpRegion(val interface{}) error {
return nil
}

func promptGcpAgentlessQuestions(
config *gcp.GenerateGcpTfConfigurationArgs,
extraState *GcpGenerateCommandExtraState,
) error {
projectFilterListInput := ""

err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{
{
Prompt: &survey.Input{Message: QuestionGcpProjectFilterList, Default: strings.Join(config.ProjectFilterList, ",")},
Response: &projectFilterListInput,
},
}, config.Agentless)

if projectFilterListInput != "" {
config.ProjectFilterList = strings.Split(projectFilterListInput, ",")
}

return err
}

func promptGcpAuditLogQuestions(
config *gcp.GenerateGcpTfConfigurationArgs,
extraState *GcpGenerateCommandExtraState,
Expand Down Expand Up @@ -703,6 +747,11 @@ func askAdvancedOptions(config *gcp.GenerateGcpTfConfigurationArgs, extraState *
// supported) is difficult when the options are dynamic (which they are)
var options []string

// Only show Advanced Agentless options if Agentless integration is set to true
if config.Agentless {
options = append(options, GcpAdvancedOptAgentless)
}

// Only show Advanced AuditLog options if AuditLog integration is set to true
if config.AuditLog {
options = append(options, GcpAdvancedOptAuditLog)
Expand All @@ -726,6 +775,10 @@ func askAdvancedOptions(config *gcp.GenerateGcpTfConfigurationArgs, extraState *

// Based on response, prompt for actions
switch answer {
case GcpAdvancedOptAgentless:
if err := promptGcpAgentlessQuestions(config, extraState); err != nil {
return err
}
case GcpAdvancedOptAuditLog:
if err := promptGcpAuditLogQuestions(config, extraState); err != nil {
return err
Expand Down Expand Up @@ -770,13 +823,9 @@ func askAdvancedOptions(config *gcp.GenerateGcpTfConfigurationArgs, extraState *
return nil
}

func configurationOrAuditLogEnabled(config *gcp.GenerateGcpTfConfigurationArgs) *bool {
auditLogOrConfigurationEnabled := config.AuditLog || config.Configuration
return &auditLogOrConfigurationEnabled
}

func gcpConfigIsEmpty(g *gcp.GenerateGcpTfConfigurationArgs) bool {
return !g.AuditLog &&
return !g.Agentless &&
!g.AuditLog &&
!g.Configuration &&
g.ServiceAccountCredentials == "" &&
g.GcpOrganizationId == "" &&
Expand Down Expand Up @@ -814,6 +863,10 @@ func promptGcpGenerate(
// These are the core questions that should be asked.
if err := SurveyMultipleQuestionWithValidation(
[]SurveyQuestionWithValidationArgs{
{
Prompt: &survey.Confirm{Message: QuestionGcpEnableAgentless, Default: config.Agentless},
Response: &config.Agentless,
},
{
Prompt: &survey.Confirm{Message: QuestionGcpEnableConfiguration, Default: config.Configuration},
Response: &config.Configuration,
Expand All @@ -826,39 +879,50 @@ func promptGcpGenerate(
return err
}

// Validate one of configuration or audit log was enabled; otherwise error out
if !config.Agentless && !config.Configuration && !config.AuditLog {
return errors.New("must enable agentless, audit log or configuration")
}

configOrAuditLogEnabled := config.Configuration || config.AuditLog
regionsInput := ""

if err := SurveyMultipleQuestionWithValidation(
[]SurveyQuestionWithValidationArgs{
{
Prompt: &survey.Input{Message: QuestionGcpProjectID, Default: config.GcpProjectId},
Checks: []*bool{configurationOrAuditLogEnabled(config)},
Opts: []survey.AskOpt{survey.WithValidator(validateGcpProjectId)},
Required: true,
Response: &config.GcpProjectId,
},
{
Prompt: &survey.Input{Message: QuestionGcpRegions, Default: strings.Join(config.Regions, ",")},
Checks: []*bool{&config.Agentless},
Response: &regionsInput,
Required: true,
},
{
Prompt: &survey.Confirm{Message: QuestionGcpOrganizationIntegration, Default: config.OrganizationIntegration},
Checks: []*bool{configurationOrAuditLogEnabled(config)},
Response: &config.OrganizationIntegration,
},
{
Prompt: &survey.Input{Message: QuestionGcpOrganizationID, Default: config.GcpOrganizationId},
Checks: []*bool{&config.OrganizationIntegration, configurationOrAuditLogEnabled(config)},
Checks: []*bool{&config.OrganizationIntegration},
Required: true,
Response: &config.GcpOrganizationId,
},
{
Prompt: &survey.Input{Message: QuestionGcpServiceAccountCredsPath, Default: config.ServiceAccountCredentials},
Checks: []*bool{configurationOrAuditLogEnabled(config)},
Opts: []survey.AskOpt{survey.WithValidator(gcp.ValidateServiceAccountCredentials)},
Checks: []*bool{&configOrAuditLogEnabled},
Response: &config.ServiceAccountCredentials,
},
}); err != nil {
return err
}

// Validate one of configuration or audit log was enabled; otherwise error out
if !config.Configuration && !config.AuditLog {
return errors.New("must enable audit log or configuration")
if regionsInput != "" {
config.Regions = strings.Split(regionsInput, ",")
}

// Find out if the customer wants to specify more advanced features
Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/generate_gcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestMissingValidGcpEntityToConfigure(t *testing.T) {
data := gcp.GenerateGcpTfConfigurationArgs{}
err := promptGcpGenerate(&data, &gcp.ExistingServiceAccountDetails{}, &GcpGenerateCommandExtraState{Output: "/tmp"})
assert.Error(t, err)
assert.Equal(t, "must enable audit log or configuration", err.Error())
assert.Equal(t, "must enable agentless, audit log or configuration", err.Error())
}

func TestGcpBucketRegionRegex(t *testing.T) {
Expand Down
Loading

0 comments on commit 4543118

Please sign in to comment.