Closed tlozoot closed 4 years ago
Cheers @tlozoot 🌮 The dataset
attribute will need to be added in cloudflare/cloudflare-go
before it can be used here and for who ever picks it up while it's being added, I'd recommend adding the Logpush dataset endpoint itself as well to ensure full coverage.
Thanks Jacob! It looks like the LogpushJob struct and API calls do support the "dataset" attribute already in the cloudflare-go library: https://github.com/cloudflare/cloudflare-go/blob/master/logpush.go#L14
Let me know if there's anything else I need to get updated there.
Ah nice, I totally missed it. That's one less thing to worry about when adding it 😄
This is great, looking forward to testing this!
Apologies @jacobbednarz but not sure if I should post this error below or open a new ticket?
I'm trying to test the logpush job but running into a problem:
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
sumologic_collector.collector: Refreshing state... [id=108448215]
sumologic_http_source.http_source: Refreshing state... [id=150364538]
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# cloudflare_logpush_job.firewall_events_job will be created
+ resource "cloudflare_logpush_job" "firewall_events_job" {
+ dataset = "firewall_events"
+ destination_conf = "sumo://endpoint1.collection.eu.sumologic.com/receiver/v1/http/(redacted)"
+ enabled = true
+ id = (known after apply)
+ logpull_options = "fields=RayID,Action,ClientIP,ClientRequestHTTPHost,ClientRequestHTTPMethodName,ClientRequestPath,ClientRequestQuery,Datetime,EdgeResponseStatus,RayName×tamps=rfc3339"
+ name = "fwevents-logpush-job"
+ ownership_challenge = "(redacted)"
+ zone_id = "(redacted)"
}
Plan: 1 to add, 0 to change, 0 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
$ terraform apply --auto-approve
sumologic_collector.collector: Refreshing state... [id=108448215]
sumologic_http_source.http_source: Refreshing state... [id=150364538]
cloudflare_logpush_job.firewall_events_job: Creating...
Error: error finding logpush job: could not extract Logpush job from resource - invalid identifier (): strconv.Atoi: parsing "": invalid syntax
on main.tf line 31, in resource "cloudflare_logpush_job" "firewall_events_job":
31: resource "cloudflare_logpush_job" "firewall_events_job" {
Here's the relevant part of the configuration:
resource "cloudflare_logpush_job" "firewall_events_job" {
name = "fwevents-logpush-job"
zone_id = var.cf_zone_id
enabled = "true"
dataset = "firewall_events"
logpull_options = "fields=RayID,Action,ClientIP,ClientRequestHTTPHost,ClientRequestHTTPMethodName,ClientRequestPath,ClientRequestQuery,Datetime,EdgeResponseStatus,RayName×tamps=rfc3339"
destination_conf = "sumo://endpoint1.collection.eu.sumologic.com/receiver/v1/http/(redacted)"
ownership_challenge = "(redacted)"
}
Here's with trace output on in case helpful:
cloudflare_logpush_job.firewall_events_job: Creating...
2020/04/20 00:21:04 [DEBUG] cloudflare_logpush_job.firewall_events_job: applying the planned Create change
2020/04/20 00:21:04 [TRACE] GRPCProvider: ApplyResourceChange
2020/04/20 00:21:04 [DEBUG] cloudflare_logpush_job.firewall_events_job: apply errored, but we're indicating that via the Error pointer rather than returning it: error finding logpush job: could not extract Logpush job from resource - invalid identifier (): strconv.Atoi: parsing "": invalid syntax
2020/04/20 00:21:04 [TRACE] <root>: eval: *terraform.EvalMaybeTainted
2020/04/20 00:21:04 [TRACE] EvalMaybeTainted: cloudflare_logpush_job.firewall_events_job encountered an error during creation, so it is now marked as tainted
2020/04/20 00:21:04 [TRACE] <root>: eval: *terraform.EvalWriteState
2020/04/20 00:21:04 [TRACE] EvalWriteState: removing state object for cloudflare_logpush_job.firewall_events_job
2020/04/20 00:21:04 [TRACE] <root>: eval: *terraform.EvalApplyProvisioners
2020/04/20 00:21:04 [TRACE] EvalApplyProvisioners: cloudflare_logpush_job.firewall_events_job has no state, so skipping provisioners
2020/04/20 00:21:04 [TRACE] <root>: eval: *terraform.EvalMaybeTainted
2020/04/20 00:21:04 [TRACE] EvalMaybeTainted: cloudflare_logpush_job.firewall_events_job encountered an error during creation, so it is now marked as tainted
2020/04/20 00:21:04 [TRACE] <root>: eval: *terraform.EvalWriteState
2020/04/20 00:21:04 [TRACE] EvalWriteState: removing state object for cloudflare_logpush_job.firewall_events_job
2020/04/20 00:21:04 [TRACE] <root>: eval: *terraform.EvalIf
2020/04/20 00:21:04 [TRACE] <root>: eval: *terraform.EvalIf
2020/04/20 00:21:04 [TRACE] <root>: eval: *terraform.EvalWriteDiff
2020/04/20 00:21:04 [TRACE] <root>: eval: *terraform.EvalApplyPost
2020/04/20 00:21:04 [ERROR] <root>: eval: *terraform.EvalApplyPost, err: error finding logpush job: could not extract Logpush job from resource - invalid identifier (): strconv.Atoi: parsing "": invalid syntax
2020/04/20 00:21:04 [ERROR] <root>: eval: *terraform.EvalSequence, err: error finding logpush job: could not extract Logpush job from resource - invalid identifier (): strconv.Atoi: parsing "": invalid syntax
2020/04/20 00:21:04 [TRACE] [walkApply] Exiting eval tree: cloudflare_logpush_job.firewall_events_job
2020/04/20 00:21:04 [TRACE] vertex "cloudflare_logpush_job.firewall_events_job": visit complete
2020/04/20 00:21:04 [TRACE] dag/walk: upstream of "provider.cloudflare (close)" errored, so skipping
2020/04/20 00:21:04 [TRACE] dag/walk: upstream of "meta.count-boundary (EachMode fixup)" errored, so skipping
2020/04/20 00:21:04 [TRACE] dag/walk: upstream of "root" errored, so skipping
2020/04/20 00:21:04 [TRACE] statemgr.Filesystem: not making a backup, because the new snapshot is identical to the old
2020/04/20 00:21:04 [TRACE] statemgr.Filesystem: no state changes since last snapshot
2020/04/20 00:21:04 [TRACE] statemgr.Filesystem: writing snapshot at terraform.tfstate
2020/04/20 00:21:04 [TRACE] statemgr.Filesystem: removing lock metadata file .terraform.tfstate.lock.info
2020/04/20 00:21:04 [TRACE] statemgr.Filesystem: unlocking terraform.tfstate using fcntl flock
Error: error finding logpush job: could not extract Logpush job from resource - invalid identifier (): strconv.Atoi: parsing "": invalid syntax
on main.tf line 31, in resource "cloudflare_logpush_job" "firewall_events_job":
31: resource "cloudflare_logpush_job" "firewall_events_job" {
2020-04-20T00:21:04.713+0100 [DEBUG] plugin: plugin process exited: path=/Users/pdonahue/src/fwe/.terraform/plugins/darwin_amd64/terraform-provider-cloudflare_v2.5.1_x4 pid=49266
2020-04-20T00:21:04.713+0100 [DEBUG] plugin: plugin exited
It looks like it's failing to find the job ID in the Create
🤔
I'll need to spin one of these up and look at debugging it. I know we've had issues in the past with automating this but not with creating it once you have the ownership_challenge
.
Looking into this, I'm not sure of if something changed since support was added or the implementation wasn't fully vetted at that point as the ownership_challenge
is required in the schema but the value can only be created using a separate API call and then read the contents of that file to provide the value to Terraform. Given this, my understanding of how the flow should work is:
destination_conf
set by the user (ownership_challenge
should not be required)GetLogpushOwnershipChallenge
cloudflare-go method which returns the file location. Create
for the resource.Based on that, I think we need to work out:
Updated the comment as my keyboard got submit happy 😞
Hi @jacobbednarz , thanks for looking into this. For the time being I've documented the API challenge calls outside the Terraform flow. I'll speak with @tlozoot about whether we want to bring those in later but for now I'd like to get customers started who already have the challenge response.
Here's the excerpt from my instructions for using log push with Sumo Logic (and note that I've tried replacing the uhh replace() function with a hardcoded variable just to make sure that wasn't the hang up :) —
Before Cloudflare will start start sending logs to your collector, you need to demonstrate the ability to read from it. This check prevents accidental (or intentional) misconfigurations.
In a new shell window—you should keep the current one with your environment variables set for use with Terraform—we'll start tailing Sumo Logic for events sent from the firewall-events-source
HTTP source. The first time that you run livetail you'll need to specify your Sumo Logic Environment, Access ID and Access Key, but these values will be stored for subsequent runs:
$ livetail _source=firewall-events-source
### Welcome to Sumo Logic Live Tail Command Line Interface ###
1 US1
2 US2
3 EU
4 AU
5 DE
6 FED
7 JP
8 CA
Please select Sumo Logic environment:
See http://help.sumologic.com/Send_Data/Collector_Management_API/Sumo_Logic_Endpoints to choose the correct environment. 3
### Authenticating ###
Please enter your Access ID: <access id>
Please enter your Access Key <access key>
### Starting Live Tail session ###
Before requesting a challenge token, we need to figure out where Cloudflare should send logs. We do this by asking Terraform for the receiver URL of the recently created HTTP source. (Note that we modify the URL returned slightly as Cloudflare Logs expects "sumo://" rather than "https://".)
$ export SUMO_RECEIVER_URL=$(terraform state show sumologic_http_source.http_source | grep url | awk '{print $3}' | sed -e 's/https:/sumo:/; s/"//g')
$ echo $SUMO_RECEIVER_URL
sumo://endpoint1.collection.eu.sumologic.com/receiver/v1/http/<redacted>
With URL in hand, we can now request the token. Unfortunately the /logpush endpoint doesn't yet support scoped API tokens (working on it!) so you'll need to specify your account's email and global API key:
$ curl -sXPOST -H "Content-Type: application/json" -H "X-Auth-Email: $CF_EMAIL" -H "X-Auth-Key: $TF_VAR_cf_api_key" -d '{"destination_conf":'''"$SUMO_RECEIVER_URL"'''}' https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/logpush/ownership
{"errors":[],"messages":[],"result":{"filename":"ownership-challenge-bb2912e6.txt","message":"","valid":true},"success":true}
Back in the other window where your livetail is running you should see something like this:
{"content":"eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIiwidHlwIjoiSldUIn0..WQhkW_EfxVy8p0BQ.oO6YEvfYFMHCTEd6D8MbmyjJqcrASDLRvHFTbZ5yUTMqBf1oniPNzo9Mn3ZzgTdayKg_jk0Gg-mBpdeqNI8LJFtUzzgTGU-aN1-haQlzmHVksEQdqawX7EZu2yiePT5QVk8RUsMrgloa76WANQbKghx1yivTZ3TGj8WquZELgnsiiQSvHqdFjAsiUJ0g73L962rDMJPG91cHuDqgfXWwSUqPsjVk88pmvGEEH4AMdKIol0EOc-7JIAWFBhCqmnv0uAXVOH5uXHHe_YNZ8PNLfYZXkw1xQlVEwH52wRC93ohIxg.pHAeaOGC8ALwLOXqxpXJgQ","filename":"ownership-challenge-bb2912e6.txt"}
Copy the content value from above into an environment variable, as you'll need it in a minute to create the job:
$ export LOGPUSH_CHALLENGE_TOKEN="<content value>"
With challenge token in hand, we'll use Terraform to create the job. First we append our Cloudflare configuration to the main.tf
$ cat <<EOF | tee -a main.tf
##################
### CLOUDFLARE ###
##################
provider "cloudflare" {
version = "~> 2.0"
email = var.cf_email
api_key = var.cf_api_key
}
resource "cloudflare_logpush_job" "firewall_events_job" {
enabled = true
name = "fwevents-logpush-job"
zone_id = var.cf_zone_id
dataset = "firewall_events"
logpull_options = "fields=RayID,Action,ClientIP,ClientRequestHTTPHost,ClientRequestHTTPMethodName,ClientRequestPath,ClientRequestQuery,Datetime,EdgeResponseStatus,RayName×tamps=rfc3339"
destination_conf = replace(sumologic_http_source.http_source.url,"https:","sumo:")
ownership_challenge = "$LOGPUSH_CHALLENGE_TOKEN"
}
EOF
And then we add variables:
$ cat <<EOF | tee -a variables.tf
##################
### CLOUDFLARE ###
##################
variable "cf_email" {
default = "$CF_EMAIL"
}
variable "cf_zone_id" {
default = "$CF_ZONE_ID"
}
# rather than set this here, and potentially commit a secret to our source code repository, we'll set the TF_VAR_cf_api_key environment variable
variable "cf_api_key" {
default = ""
}
EOF
Is there any way to have Terraform dump the underlying API calls it's making (via the golang SDK)?
@jacobbednarz good news! @patryk helped me debug just now and I've got it working. Thanks for all your help. Should have a blog post out soon on the new functionality.
Is there any way to have Terraform dump the underlying API calls it's making (via the golang SDK)?
Just for posterity sake in case someone else stumbles on this, prepending the Terraform command with TF_LOG=DEBUG
outputs the HTTP traffic and internal logging.
Awesome to hear! Looks like the Sumo collector is unique here as you can tail the incoming payloads which we can’t unfortunately do for other providers. Looking forward to the blog post and hearing from @tlozoot on whether we can improve this process. Without it, we might only be able to support manual setup (like you’ve mentioned) or import only resources from the UI.
Here's the documented API for cloudflare_logpush_job: https://www.terraform.io/docs/providers/cloudflare/r/logpush_job.html
It allows the following arguments:
Cloudflare's API is documented here: https://api.cloudflare.com/#logpush-jobs-create-logpush-job and also contains the "dataset" parameter. This is important for choosing between HTTP requests, Spectrum events, and soon other logs like Firewall and Browser Insight.
There is additional documentation about Logpush jobs here: https://developers.cloudflare.com/logs/logpush/logpush-configuration-api/understanding-logpush-api/