Skip to content

Commit

Permalink
Add gcp org-access-logs (#32)
Browse files Browse the repository at this point in the history
This is a new feature in P0's Google Gcloud installation. Add supporting
resource.
  • Loading branch information
nbrahms authored Jun 7, 2024
1 parent d9baf7c commit 369ab83
Show file tree
Hide file tree
Showing 7 changed files with 406 additions and 6 deletions.
73 changes: 73 additions & 0 deletions docs/resources/gcp_access_logs.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,80 @@ Use the read-only attributes defined on `p0_gcp` to create the requisite Google

P0 recommends defining this infrastructure according to the example usage pattern.

## Example Usage

```terraform
resource "p0_gcp" "example" {
organization_id = "123456789012"
}
locals {
project = "my_project_id"
}
# Follow instructions for creating Terraform for IAM assessment in p0_gcp_iam_assessment documentation
# ...
resource "p0_gcp_iam_assessment" "example" {
project = locals.project
depends_on = [google_project_iam_member.example]
}
resource "google_project_iam_audit_config" "example" {
project = locals.project
service = "allServices"
audit_log_config {
log_type = "ADMIN_READ"
}
audit_log_config {
log_type = "DATA_READ"
}
audit_log_config {
log_type = "DATA_WRITE"
}
}
# Data access logs are sent to P0 using this Pub/Sub topic
resource "google_pubsub_topic" "example" {
project = locals.project
name = p0_gcp.example.access_logs.pub_sub.topic_id
}
# The log sink that writes to the P0 access-logging Pub/Sub topic
resource "google_logging_project_sink" "example" {
project = locals.project
name = p0_gcp.example.access_logs.logging.sink_id
destination = "pubsub.googleapis.com/projects/my_project/topics/${google_pubsub_topic.example.name}"
description = "P0 data access log sink"
filter = p0_gcp.example.access_logs.logging.filter
}
# Grants the logging service account permission to write to the access-logging Pub/Sub topic
resource "google_pubsub_topic_iam_member" "logging_example" {
project = locals.project
role = p0_gcp.example.access_logs.logging.role
topic = google_pubsub_topic.example.name
member = google_logging_project_sink.example.writer_identity
}
# Grants P0 permission to read from the access-logging Pub/Sub topic
resource "google_pubsub_topic_iam_member" "p0_example" {
project = locals.project
role = p0_gcp.example.access_logs.predefined_role
topic = google_pubsub_topic.example.name
member = "serviceAccount:${p0_gcp.example.serviceAccountEmail}"
}
# Finish the P0 access-logs installation
resource "p0_gcp_access_logs" "example" {
project = locals.project
depends_on = [
google_logging_project_sink.example,
google_pubsub_topic_iam_member.p0_example
]
}
```

<!-- schema generated by tfplugindocs -->
## Schema
Expand Down
110 changes: 110 additions & 0 deletions docs/resources/gcp_organization_access_logs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "p0_gcp_organization_access_logs Resource - p0"
subcategory: ""
description: |-
An installation of P0, on an entire Google Cloud organization, for access-log collection,
which enhances IAM assessment. Note that P0 will have access to logs from all your projects, not just those
configured for IAM assessment.
To use this resource, you must also:
create a Pub/Sub topic,create an organization logging sink, publishing to this topic,grant your logging service account permissions to publish to this Pub/Sub topic, andgrant P0 the ability to subscribe to this Pub/Sub topic.
Use the read-only attributes defined on p0_gcp to create the requisite Google Cloud infrastructure.
P0 recommends defining this infrastructure according to the example usage pattern.
---

# p0_gcp_organization_access_logs (Resource)

An installation of P0, on an entire Google Cloud organization, for access-log collection,
which enhances IAM assessment. Note that P0 will have access to logs from all your projects, not just those
configured for IAM assessment.

To use this resource, you must also:
- create a Pub/Sub topic,
- create an organization logging sink, publishing to this topic,
- grant your logging service account permissions to publish to this Pub/Sub topic, and
- grant P0 the ability to subscribe to this Pub/Sub topic.

Use the read-only attributes defined on `p0_gcp` to create the requisite Google Cloud infrastructure.

P0 recommends defining this infrastructure according to the example usage pattern.

## Example Usage

```terraform
resource "p0_gcp" "example" {
organization_id = "123456789012"
}
locals {
logs_topic_project = "my-logs-project"
}
resource "google_organization_iam_audit_config" "example" {
org_id = p0_gcp.example.org_id
service = "allServices"
audit_log_config {
log_type = "ADMIN_READ"
}
audit_log_config {
log_type = "DATA_READ"
}
audit_log_config {
log_type = "DATA_WRITE"
}
}
# Data access logs are sent to P0 using this Pub/Sub topic
resource "google_pubsub_topic" "example" {
project = locals.logs_topic_project
name = p0_gcp.example.access_logs.pub_sub.topic_id
}
# The log sink that writes to the P0 access-logging Pub/Sub topic
resource "google_logging_organization_sink" "example" {
org_id = p0_gcp.example.org_id
name = p0_gcp.example.access_logs.logging.sink_id
destination = "pubsub.googleapis.com/projects/${locals.logs_topic_project}/topics/${google_pubsub_topic.example.name}"
description = "P0 data access log sink"
filter = p0_gcp.example.access_logs.logging.filter
}
# Grants the logging service account permission to write to the access-logging Pub/Sub topic
resource "google_pubsub_topic_iam_member" "logging_example" {
project = locals.logs_topic_project
role = p0_gcp.example.access_logs.logging.role
topic = google_pubsub_topic.example.name
member = google_logging_organization_sink.example.writer_identity
}
# Grants P0 permission to read from the access-logging Pub/Sub topic
resource "google_pubsub_topic_iam_member" "p0_example" {
project = locals.logs_topic_project
role = p0_gcp.example.access_logs.predefined_role
topic = google_pubsub_topic.example.name
member = "serviceAccount:${p0_gcp.example.serviceAccountEmail}"
}
# Install organization access logging in P0
resource "p0_gcp_access_logs" "example" {
topic_project_id = locals.logs_topic_project
depends_on = [
google_logging_project_sink.example,
google_pubsub_topic_iam_member.p0_example
]
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `topic_project_id` (String) The project identifier where the access-logs Pub/Sub topic should reside

### Read-Only

- `state` (String) This item's install progress in the P0 application:
- 'stage': The item has been staged for installation
- 'configure': The item is available to be added to P0, and may be configured
- 'installed': The item is fully installed
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ resource "google_pubsub_topic_iam_member" "logging_example" {
project = locals.project
role = p0_gcp.example.access_logs.logging.role
topic = google_pubsub_topic.example.name
member = "your logging service account email"
member = google_logging_project_sink.example.writer_identity
}

# Grants P0 permission to read from the access-logging Pub/Sub topic
Expand Down
62 changes: 62 additions & 0 deletions examples/resources/p0_gcp_organization_access_logs/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
resource "p0_gcp" "example" {
organization_id = "123456789012"
}

locals {
logs_topic_project = "my-logs-project"
}

resource "google_organization_iam_audit_config" "example" {
org_id = p0_gcp.example.org_id
service = "allServices"
audit_log_config {
log_type = "ADMIN_READ"
}
audit_log_config {
log_type = "DATA_READ"
}
audit_log_config {
log_type = "DATA_WRITE"
}
}

# Data access logs are sent to P0 using this Pub/Sub topic
resource "google_pubsub_topic" "example" {
project = locals.logs_topic_project
name = p0_gcp.example.access_logs.pub_sub.topic_id
}

# The log sink that writes to the P0 access-logging Pub/Sub topic
resource "google_logging_organization_sink" "example" {
org_id = p0_gcp.example.org_id
name = p0_gcp.example.access_logs.logging.sink_id
destination = "pubsub.googleapis.com/projects/${locals.logs_topic_project}/topics/${google_pubsub_topic.example.name}"
description = "P0 data access log sink"

filter = p0_gcp.example.access_logs.logging.filter
}

# Grants the logging service account permission to write to the access-logging Pub/Sub topic
resource "google_pubsub_topic_iam_member" "logging_example" {
project = locals.logs_topic_project
role = p0_gcp.example.access_logs.logging.role
topic = google_pubsub_topic.example.name
member = google_logging_organization_sink.example.writer_identity
}

# Grants P0 permission to read from the access-logging Pub/Sub topic
resource "google_pubsub_topic_iam_member" "p0_example" {
project = locals.logs_topic_project
role = p0_gcp.example.access_logs.predefined_role
topic = google_pubsub_topic.example.name
member = "serviceAccount:${p0_gcp.example.serviceAccountEmail}"
}

# Install organization access logging in P0
resource "p0_gcp_access_logs" "example" {
topic_project_id = locals.logs_topic_project
depends_on = [
google_logging_project_sink.example,
google_pubsub_topic_iam_member.p0_example
]
}
3 changes: 2 additions & 1 deletion internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,15 @@ func (p *P0Provider) Configure(ctx context.Context, req provider.ConfigureReques
func (p *P0Provider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{
resources.NewRoutingRules,
installaws.NewIamWriteStagedAws,
installaws.NewAwsIamWrite,
installaws.NewIamWriteStagedAws,
installgcp.NewGcp,
installgcp.NewGcpAccessLogs,
installgcp.NewGcpIamAssessment,
installgcp.NewGcpIamAssessmentStaged,
installgcp.NewGcpIamWrite,
installgcp.NewGcpIamWriteStaged,
installgcp.NewGcpOrgAccessLogs,
installgcp.NewGcpSharingRestriction,
installssh.NewSshAwsIamWrite,
installssh.NewSshGcpIamWrite,
Expand Down
23 changes: 19 additions & 4 deletions internal/provider/resources/install/gcp/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import (
"context"
"regexp"

"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/p0-security/terraform-provider-p0/internal"
installresources "github.com/p0-security/terraform-provider-p0/internal/provider/resources/install"
Expand Down Expand Up @@ -45,6 +47,7 @@ type gcpItemApi struct {
const (
AccessLogs = "access-logs"
GcpKey = "gcloud"
OrgAccessLogs = "org-access-logs"
SharingRestriction = "sharing-restriction"
)

Expand All @@ -71,6 +74,15 @@ var predefinedRole = schema.StringAttribute{
MarkdownDescription: `The predefined role that should be granted to P0, in order to install projects for IAM management`,
}

var projectValidators = []validator.String{
stringvalidator.RegexMatches(GcpProjectIdRegex, "GCP project IDs should consist only of alphanumeric characters and hyphens"),
}

var stateAttribute = schema.StringAttribute{
Computed: true,
MarkdownDescription: installresources.StateMarkdownDescription,
}

var itemAttributes = map[string]schema.Attribute{
// In P0 we would name this 'id' or 'project_id'; it is named 'project' here to align with Terraform's naming for
// Google Cloud resources
Expand All @@ -80,11 +92,9 @@ var itemAttributes = map[string]schema.Attribute{
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
Validators: projectValidators,
},
"state": schema.StringAttribute{
Computed: true,
MarkdownDescription: installresources.StateMarkdownDescription,
},
"state": stateAttribute,
}

func permissions(name string) schema.ListAttribute {
Expand Down Expand Up @@ -143,3 +153,8 @@ func newItemInstaller(component string, providerData *internal.P0ProviderData) *
ToJson: itemToJson,
}
}

func singletonGetId(data any) *string {
key := installresources.SingletonKey
return &key
}
Loading

0 comments on commit 369ab83

Please sign in to comment.