diff --git a/cli/README.md b/cli/README.md index 0997d52f7..a764a52f1 100644 --- a/cli/README.md +++ b/cli/README.md @@ -199,7 +199,6 @@ locally you need to setup the following environment variables and use the direct `make integration`, an example of the command you can use is: ``` CI_ACCOUNT="" \ - CI_V1_ACCOUNT="" \ CI_SUBACCOUNT="" \ CI_API_KEY="" \ CI_API_SECRET="" \ @@ -210,8 +209,6 @@ This is a list of all environment variables used in the running the integration | Environment Variable | Description | |----------------------|-------------| |`CI_ACCOUNT=""` | account subdomain of URL (i.e. `.lacework.net`)| -|`CI_V1_ACCOUNT=""` | for standalone accounts use the same as `CI_ACCOUNT`, for organizations -use `CI_SUBACCOUNT`| |`CI_SUBACCOUNT=""` | (orgs only) a sub-account| |`CI_API_KEY=""` | API access key id| |`CI_API_SECRET=""` | API secret access key| diff --git a/cli/cmd/generate_gcp.go b/cli/cmd/generate_gcp.go index ac404d37b..0ea465189 100644 --- a/cli/cmd/generate_gcp.go +++ b/cli/cmd/generate_gcp.go @@ -29,16 +29,20 @@ var ( QuestionExistingServiceAccountName = "Specify an existing service account name:" QuestionExistingServiceAccountPrivateKey = "Specify an existing service account private key (base64 encoded):" - GcpAdvancedOptAuditLog = "Configure additional Audit Log options" - QuestionGcpUseExistingBucket = "Use an existing bucket?" - QuestionGcpExistingBucketName = "Specify an existing bucket name:" - QuestionGcpConfigureNewBucket = "Configure settings for new bucket?" - QuestionGcpBucketRegion = "Specify the bucket region: (optional)" - QuestionGcpCustomBucketName = "Specify a custom bucket name: (optional)" - QuestionGcpBucketLifecycle = "Specify the bucket lifecycle rule age: (optional)" - QuestionGcpEnableUBLA = "Enable uniform bucket level access(UBLA)?" - QuestionGcpUseExistingSink = "Use an existing sink?" - QuestionGcpExistingSinkName = "Specify the existing sink name" + GcpAdvancedOptAuditLog = "Configure additional Audit Log options" + QuestionGcpUseExistingBucket = "Use an existing bucket?" + QuestionGcpExistingBucketName = "Specify an existing bucket name:" + QuestionGcpConfigureNewBucket = "Configure settings for new bucket?" + QuestionGcpBucketRegion = "Specify the bucket region: (optional)" + QuestionGcpCustomBucketName = "Specify a custom bucket name: (optional)" + QuestionGcpBucketLifecycle = "Specify the bucket lifecycle rule age: (optional)" + QuestionGcpEnableUBLA = "Enable uniform bucket level access(UBLA)?" + QuestionGcpUseExistingSink = "Use an existing sink?" + QuestionGcpExistingSinkName = "Specify the existing sink name:" + QuestionGcpUseExistingPubSubTopic = "Use an existing Pub/Sub topic?" + QuestionGcpExistingPubSubTopicId = "Specify the existing Pub/Sub topic ID:" + QuestionGcpUseExistingPubSubSubscription = "Use an existing Pub/Sub subscription?" + QuestionGcpExistingPubSubSubscriptionName = "Specify the existing Pub/Sub subscription name:" GcpAdvancedOptIntegrationName = "Customize integration name(s)" QuestionGcpConfigurationIntegrationName = "Specify a custom configuration integration name: (optional)" @@ -112,6 +116,8 @@ See help output for more details on the parameter value(s) required for Terrafor gcp.WithBucketRegion(GenerateGcpCommandState.BucketRegion), gcp.WithExistingLogBucketName(GenerateGcpCommandState.ExistingLogBucketName), gcp.WithExistingLogSinkName(GenerateGcpCommandState.ExistingLogSinkName), + gcp.WithExistingPubSubTopicId(GenerateGcpCommandState.ExistingPubSubTopicId), + gcp.WithExistingPubSubSubscriptionName(GenerateGcpCommandState.ExistingPubSubSubscriptionName), gcp.WithAuditLogIntegrationName(GenerateGcpCommandState.AuditLogIntegrationName), gcp.WithLaceworkProfile(GenerateGcpCommandState.LaceworkProfile), gcp.WithLogBucketLifecycleRuleAge(GenerateGcpCommandState.LogBucketLifecycleRuleAge), @@ -274,13 +280,15 @@ See help output for more details on the parameter value(s) required for Terrafor ) type GcpGenerateCommandExtraState struct { - AskAdvanced bool - Output string - ConfigureNewBucketSettings bool - UseExistingServiceAccount bool - UseExistingBucket bool - UseExistingSink bool - TerraformApply bool + AskAdvanced bool + Output string + ConfigureNewBucketSettings bool + UseExistingServiceAccount bool + UseExistingBucket bool + UseExistingSink bool + UseExistingPubSubTopic bool + UseExistingPubSubSubscription bool + TerraformApply bool } func (gcp *GcpGenerateCommandExtraState) isEmpty() bool { @@ -289,6 +297,8 @@ func (gcp *GcpGenerateCommandExtraState) isEmpty() bool { !gcp.UseExistingServiceAccount && !gcp.UseExistingBucket && !gcp.UseExistingSink && + !gcp.UseExistingPubSubTopic && + !gcp.UseExistingPubSubSubscription && !gcp.TerraformApply } @@ -368,6 +378,16 @@ func initGenerateGcpTfCommandFlags() { "existing_sink_name", "", "specify existing sink name") + generateGcpTfCommand.PersistentFlags().StringVar( + &GenerateGcpCommandState.ExistingPubSubTopicId, + "existing_pub_sub_topic_id", + "", + "specify existing pub/sub topic id") + generateGcpTfCommand.PersistentFlags().StringVar( + &GenerateGcpCommandState.ExistingPubSubSubscriptionName, + "existing_pub_sub_subscription_name", + "", + "specify existing pub/sub subscription name") // DEPRECATED generateGcpTfCommand.PersistentFlags().BoolVar( @@ -517,6 +537,33 @@ func promptGcpAuditLogQuestions( Required: true, Response: &config.ExistingLogSinkName, }, + { + Prompt: &survey.Confirm{Message: QuestionGcpUseExistingPubSubTopic, + Default: extraState.UseExistingPubSubTopic}, + Checks: []*bool{&config.AuditLog, &config.UsePubSubAudit}, + Required: true, + Response: &extraState.UseExistingPubSubTopic, + }, + { + Prompt: &survey.Input{Message: QuestionGcpExistingPubSubTopicId, Default: config.ExistingPubSubTopicId}, + Checks: []*bool{&config.AuditLog, &config.UsePubSubAudit, &extraState.UseExistingPubSubTopic}, + Required: true, + Response: &config.ExistingPubSubTopicId, + }, + { + Prompt: &survey.Confirm{Message: QuestionGcpUseExistingPubSubSubscription, + Default: extraState.UseExistingPubSubSubscription}, + Checks: []*bool{&config.AuditLog, &config.UsePubSubAudit}, + Required: true, + Response: &extraState.UseExistingPubSubSubscription, + }, + { + Prompt: &survey.Input{Message: QuestionGcpExistingPubSubSubscriptionName, + Default: config.ExistingPubSubSubscriptionName}, + Checks: []*bool{&config.AuditLog, &config.UsePubSubAudit, &extraState.UseExistingPubSubSubscription}, + Required: true, + Response: &config.ExistingPubSubSubscriptionName, + }, { Prompt: &survey.Input{Message: QuestionGcpCustomFilter, Default: config.CustomFilter}, Checks: []*bool{&config.AuditLog}, diff --git a/cli/docs/lacework_generate_cloud-account_gcp.md b/cli/docs/lacework_generate_cloud-account_gcp.md index 08048f4c1..53c08826b 100644 --- a/cli/docs/lacework_generate_cloud-account_gcp.md +++ b/cli/docs/lacework_generate_cloud-account_gcp.md @@ -48,6 +48,8 @@ lacework generate cloud-account gcp [flags] --custom_filter string Audit Log filter which supersedes all other filter options when defined --enable_ubla enable universal bucket level access(ubla) (default true) --existing_bucket_name string specify existing bucket name + --existing_pub_sub_subscription_name string specify existing pub/sub subscription name + --existing_pub_sub_topic_id string specify existing pub/sub topic id --existing_service_account_name string specify existing service account name --existing_service_account_private_key string specify existing service account private key (base64 encoded) --existing_sink_name string specify existing sink name diff --git a/integration/gcp_generation_test.go b/integration/gcp_generation_test.go index 8c5a55b96..e9740e06e 100644 --- a/integration/gcp_generation_test.go +++ b/integration/gcp_generation_test.go @@ -161,6 +161,8 @@ func TestGenerationPubSubAuditlogOnlyGcp(t *testing.T) { MsgMenu{cmd.GcpAdvancedOptAuditLog, 0}, MsgRsp{cmd.QuestionUsePubSubAudit, "y"}, MsgRsp{cmd.QuestionGcpUseExistingSink, "n"}, + MsgRsp{cmd.QuestionGcpUseExistingPubSubTopic, "n"}, + MsgRsp{cmd.QuestionGcpUseExistingPubSubSubscription, "n"}, MsgRsp{cmd.QuestionGcpCustomFilter, ""}, MsgRsp{cmd.QuestionGcpAnotherAdvancedOpt, "n"}, MsgRsp{cmd.QuestionRunTfPlan, "n"}, @@ -181,6 +183,52 @@ func TestGenerationPubSubAuditlogOnlyGcp(t *testing.T) { assert.Equal(t, buildTf, tfResult) } +// Test pub-sub audit log with existing topic and subscription +func TestGenerationPubSubAuditLogExistingTopicAndSubscriptionGcp(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + + tfResult := runGcpGenerateTest(t, + func(c *expect.Console) { + expectsCliOutput(t, c, []MsgRspHandler{ + MsgRsp{cmd.QuestionGcpEnableConfiguration, "n"}, + MsgRsp{cmd.QuestionGcpEnableAuditLog, "y"}, + MsgRsp{cmd.QuestionGcpProjectID, projectId}, + MsgRsp{cmd.QuestionGcpOrganizationIntegration, "n"}, + MsgRsp{cmd.QuestionGcpServiceAccountCredsPath, ""}, + MsgRsp{cmd.QuestionGcpConfigureAdvanced, "y"}, + MsgMenu{cmd.GcpAdvancedOptAuditLog, 0}, + MsgRsp{cmd.QuestionUsePubSubAudit, "y"}, + MsgRsp{cmd.QuestionGcpUseExistingSink, "y"}, + MsgRsp{cmd.QuestionGcpExistingSinkName, "sink"}, + MsgRsp{cmd.QuestionGcpUseExistingPubSubTopic, "y"}, + MsgRsp{cmd.QuestionGcpExistingPubSubTopicId, "topic"}, + MsgRsp{cmd.QuestionGcpUseExistingPubSubSubscription, "y"}, + MsgRsp{cmd.QuestionGcpExistingPubSubSubscriptionName, "subscription"}, + MsgRsp{cmd.QuestionGcpCustomFilter, ""}, + MsgRsp{cmd.QuestionGcpAnotherAdvancedOpt, "n"}, + MsgRsp{cmd.QuestionRunTfPlan, "n"}, + }) + + final, _ = c.ExpectEOF() + }, + "generate", + "cloud-account", + "gcp", + ) + + assertTerraformSaved(t, final) + + buildTf, _ := gcp.NewTerraform(false, true, true, + gcp.WithProjectId(projectId), + gcp.WithExistingLogSinkName("sink"), + gcp.WithExistingPubSubTopicId("topic"), + gcp.WithExistingPubSubSubscriptionName("subscription"), + ).Generate() + assert.Equal(t, buildTf, tfResult) +} + func TestGenerationPubSubAuditlogOrgGcp(t *testing.T) { os.Setenv("LW_NOCACHE", "true") defer os.Setenv("LW_NOCACHE", "") @@ -199,6 +247,8 @@ func TestGenerationPubSubAuditlogOrgGcp(t *testing.T) { MsgMenu{cmd.GcpAdvancedOptAuditLog, 0}, MsgRsp{cmd.QuestionUsePubSubAudit, "y"}, MsgRsp{cmd.QuestionGcpUseExistingSink, "n"}, + MsgRsp{cmd.QuestionGcpUseExistingPubSubTopic, "n"}, + MsgRsp{cmd.QuestionGcpUseExistingPubSubSubscription, "n"}, MsgRsp{cmd.QuestionGcpCustomFilter, ""}, MsgRsp{cmd.QuestionGcpAnotherAdvancedOpt, "n"}, MsgRsp{cmd.QuestionRunTfPlan, "n"}, @@ -692,6 +742,8 @@ func TestGenerationPubSubUseExistingSA(t *testing.T) { MsgMenu{cmd.GcpAdvancedOptAuditLog, 0}, MsgRsp{cmd.QuestionUsePubSubAudit, "y"}, MsgRsp{cmd.QuestionGcpUseExistingSink, "n"}, + MsgRsp{cmd.QuestionGcpUseExistingPubSubTopic, "n"}, + MsgRsp{cmd.QuestionGcpUseExistingPubSubSubscription, "n"}, MsgRsp{cmd.QuestionGcpCustomFilter, ""}, MsgRsp{cmd.QuestionGcpAnotherAdvancedOpt, "y"}, MsgMenu{cmd.GcpAdvancedOptAuditLog, 1}, diff --git a/integration/gke_generation_test.go b/integration/gke_generation_test.go index c1077e83f..32897af5a 100644 --- a/integration/gke_generation_test.go +++ b/integration/gke_generation_test.go @@ -169,7 +169,7 @@ func TestGenerationExistingSink(t *testing.T) { MsgRsp{cmd.QuestionGcpConfigureAdvanced, "y"}, MsgMenu{cmd.GkeAdvancedOpt, 0}, MsgRsp{cmd.QuestionGcpUseExistingSink, "y"}, - MsgRsp{cmd.QuestionGcpExistingSinkName, "sink"}, + MsgRsp{cmd.QuestionGkeExistingSinkName, "sink"}, MsgRsp{cmd.QuestionGcpAnotherAdvancedOpt, "n"}, MsgRsp{cmd.QuestionRunTfPlan, "n"}, }) diff --git a/integration/test_resources/help/generate_cloud-account_gcp b/integration/test_resources/help/generate_cloud-account_gcp index c02398f06..aae3e28fd 100644 --- a/integration/test_resources/help/generate_cloud-account_gcp +++ b/integration/test_resources/help/generate_cloud-account_gcp @@ -32,6 +32,8 @@ Flags: --custom_filter string Audit Log filter which supersedes all other filter options when defined --enable_ubla enable universal bucket level access(ubla) (default true) --existing_bucket_name string specify existing bucket name + --existing_pub_sub_subscription_name string specify existing pub/sub subscription name + --existing_pub_sub_topic_id string specify existing pub/sub topic id --existing_service_account_name string specify existing service account name --existing_service_account_private_key string specify existing service account private key (base64 encoded) --existing_sink_name string specify existing sink name diff --git a/lwgenerate/_examples/gcp/main.go b/lwgenerate/_examples/gcp/main.go index 352d2bd82..4d3b61854 100644 --- a/lwgenerate/_examples/gcp/main.go +++ b/lwgenerate/_examples/gcp/main.go @@ -55,6 +55,24 @@ func existingGcpBucketAndSink() { fmt.Printf("\n-----\n%s", hcl) } +func existingTopicAndSubscription() { + hcl, err := gcp.NewTerraform( + true, + true, + gcp.WithProjectId("example_project"), + gcp.WithGcpServiceAccountCredentials("path/to/service/account/creds.json"), + gcp.WithExistingPubSubTopicId("existing_pub_sub_topic_id"), + gcp.WithExistingPubSubSubscriptionName("existing_pub_sub_subscription_name"), + ).Generate() + + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + + fmt.Printf("\n-----\n%s", hcl) +} + func gcpWithLaceworkProfile() { hcl, err := gcp.NewTerraform( true, @@ -126,6 +144,7 @@ func main() { basic() existingGcpServiceAccount() existingGcpBucketAndSink() + existingTopicAndSubscription() gcpWithLaceworkProfile() configOnly() auditLogOnly() diff --git a/lwgenerate/gcp/gcp.go b/lwgenerate/gcp/gcp.go index 62544d69e..8f2bcf49e 100644 --- a/lwgenerate/gcp/gcp.go +++ b/lwgenerate/gcp/gcp.go @@ -97,6 +97,12 @@ type GenerateGcpTfConfigurationArgs struct { // Existing Sink Name ExistingLogSinkName string + // Existing Topic Name + ExistingPubSubTopicId string + + // Existing Subscription Name + ExistingPubSubSubscriptionName string + // Should we force destroy the bucket if it has stuff in it? (only relevant on new Audit Log creation) // DEPRECATED EnableForceDestroyBucket bool @@ -307,6 +313,20 @@ func WithExistingLogSinkName(name string) GcpTerraformModifier { } } +// WithExistingPubSubTopicId Set the Topic ID of an existing Audit Log Topic Setup +func WithExistingPubSubTopicId(name string) GcpTerraformModifier { + return func(c *GenerateGcpTfConfigurationArgs) { + c.ExistingPubSubTopicId = name + } +} + +// WithExistingPubSubSubscriptionName Set the Topic Subscription Name of an existing Audit Log Subscription Setup +func WithExistingPubSubSubscriptionName(name string) GcpTerraformModifier { + return func(c *GenerateGcpTfConfigurationArgs) { + c.ExistingPubSubSubscriptionName = name + } +} + // WithEnableUBLA Enable force destroy of the bucket if it has stuff in it func WithEnableUBLA(enable bool) GcpTerraformModifier { return func(c *GenerateGcpTfConfigurationArgs) { @@ -597,6 +617,14 @@ func createAuditLog(args *GenerateGcpTfConfigurationArgs) (*hclwrite.Block, erro attributes["existing_sink_name"] = args.ExistingLogSinkName } + if args.ExistingPubSubTopicId != "" { + attributes["existing_pub_sub_topic_id"] = args.ExistingPubSubTopicId + } + + if args.ExistingPubSubSubscriptionName != "" { + attributes["existing_pub_sub_subscription_name"] = args.ExistingPubSubSubscriptionName + } + // default to using the project level module auditLogModuleName := "gcp_project_audit_log" @@ -605,7 +633,7 @@ func createAuditLog(args *GenerateGcpTfConfigurationArgs) (*hclwrite.Block, erro // if organization integration is true, override configModuleName to use the organization level module configurationModuleName = "gcp_organization_level_config" auditLogModuleName = "gcp_organization_level_audit_log" - // Determine if this is the a pub-sub audit log + // Determine if this is a pub-sub audit log if args.UsePubSubAudit { attributes["integration_type"] = "ORGANIZATION" } else { diff --git a/lwgenerate/gcp/gcp_test.go b/lwgenerate/gcp/gcp_test.go index 6d82c26aa..39dda07b3 100644 --- a/lwgenerate/gcp/gcp_test.go +++ b/lwgenerate/gcp/gcp_test.go @@ -141,6 +141,35 @@ func TestGenerateGcpTfConfigurationArgs_Generate_AuditLog(t *testing.T) { ), ReqProvider(projectName, moduleImportProjectLevelPubSubAuditLogExistingLogSinkName), }, + { + "TestGenerationProjectLevelPubSubAuditLogExistingPubSubTopicId", + gcp.NewTerraform(false, true, true, + gcp.WithGcpServiceAccountCredentials("/path/to/credentials"), + gcp.WithProjectId(projectName), + gcp.WithExistingPubSubTopicId("topicMcTopicFace"), + ), + ReqProvider(projectName, moduleImportProjectLevelPubSubAuditLogExistingPubSubTopicId), + }, + { + "TestGenerationProjectLevelPubSubAuditLogExistingPubSubSubscriptionName", + gcp.NewTerraform(false, true, true, + gcp.WithGcpServiceAccountCredentials("/path/to/credentials"), + gcp.WithProjectId(projectName), + gcp.WithExistingPubSubSubscriptionName("subscriptionMcSubscriptionFace"), + ), + ReqProvider(projectName, moduleImportProjectLevelPubSubAuditLogExistingPubSubSubscriptionName), + }, + { + "TestGenerationProjectLevelPubSubAuditLogExistingSinkAndExistingPubSubResources", + gcp.NewTerraform(false, true, true, + gcp.WithGcpServiceAccountCredentials("/path/to/credentials"), + gcp.WithProjectId(projectName), + gcp.WithExistingPubSubSubscriptionName("subscriptionMcSubscriptionFace"), + gcp.WithExistingPubSubTopicId("topicMcTopicFace"), + gcp.WithExistingLogSinkName("fooSink"), + ), + ReqProvider(projectName, moduleImportProjectLevelPubSubAuditLogExistingSinkAndExistingPubSubResources), + }, {"TestGenerationProjectLevelAuditLogEnableUBLA", gcp.NewTerraform(false, true, false, gcp.WithGcpServiceAccountCredentials("/path/to/credentials"), @@ -882,6 +911,29 @@ var moduleImportProjectLevelPubSubAuditLogExistingLogSinkName = `module "gcp_pro } ` +var moduleImportProjectLevelPubSubAuditLogExistingPubSubTopicId = `module "gcp_project_audit_log" { + source = "lacework/pub-sub-audit-log/gcp" + version = "~> 0.2" + existing_pub_sub_topic_id = "topicMcTopicFace" +} +` + +var moduleImportProjectLevelPubSubAuditLogExistingPubSubSubscriptionName = `module "gcp_project_audit_log" { + source = "lacework/pub-sub-audit-log/gcp" + version = "~> 0.2" + existing_pub_sub_subscription_name = "subscriptionMcSubscriptionFace" +} +` + +var moduleImportProjectLevelPubSubAuditLogExistingSinkAndExistingPubSubResources = `module "gcp_project_audit_log" { + source = "lacework/pub-sub-audit-log/gcp" + version = "~> 0.2" + existing_pub_sub_subscription_name = "subscriptionMcSubscriptionFace" + existing_pub_sub_topic_id = "topicMcTopicFace" + existing_sink_name = "fooSink" +} +` + var moduleImportProjectLevelAuditLogEnableUBLA = `module "gcp_project_audit_log" { source = "lacework/audit-log/gcp" version = "~> 3.4"