Skip to content
Notifications
Clear all

Step-by-step: Integrating Cloudflare Zero Trust logs for access control evidence.

3 Posts
3 Users
0 Reactions
5 Views
(@johndoe82)
Trusted Member
Joined: 1 week ago
Posts: 45
Topic starter   [#6248]

Hey folks,

I've been deep in the weeds on a Secureframe implementation for a client who uses Cloudflare Zero Trust (formerly Cloudflare Access) for securing their internal apps. One of the trickier parts of the audit prep was providing continuous evidence for the "Access Control" requirements, specifically around login events and policy decisions. Secureframe's native integrations didn't quite cover the granularity we needed from Cloudflare at the time.

So, I built a pipeline to get Cloudflare Zero Trust logs into Secureframe as custom evidence. It took some tinkering, but it's running smoothly now. Here's my step-by-step approach, focusing on the Terraform and Ansible pieces.

### The Goal
Automatically ingest Cloudflare Zero Trust audit logs, parse them for relevant events (like successful/failed logins, policy evaluations), and upload them to Secureframe's custom evidence API on a daily schedule.

### Step 1: Set up the Cloudflare Logpush Job
First, you need to get the logs flowing out of Cloudflare. I used Terraform to configure the Logpush job for the `zero_trust_audit_logs` dataset. You'll need appropriate API permissions.

```hcl
# terraform/modules/cloudflare_logpush/main.tf
resource "cloudflare_logpush_job" "zero_trust_audits" {
zone_id = var.cloudflare_zone_id
name = "Secureframe - Zero Trust Audit Logs"

dataset = "zero_trust_audit_logs"
frequency = "high" # Near real-time
logpull_options = "fields=Event,UserEmail,IPAddress,ActionResult,CreatedAt&timestamps=rfc3339"

destination_conf = "s3://${aws_s3_bucket.logs_bucket.bucket}/${var.s3_prefix}?region=us-east-1&sse=AES256"
}
```

This sends logs to an S3 bucket. The `logpull_options` are crucial—filter fields to only what you need for evidence.

### Step 2: Process and Transform Logs
Logs land in S3 as NDJSON. A scheduled Ansible playbook (running from a CI/CD runner or a lightweight compute instance) picks them up, filters for critical events, and formats them for Secureframe's API.

```yaml
# ansible/roles/secureframe-evidence/tasks/process_logs.yml
- name: Fetch new Cloudflare log files from S3
aws_s3:
bucket: "{{ evidence_bucket }}"
prefix: "{{ s3_prefix }}"
dest: "/tmp/raw_logs/"
mode: get

- name: Parse and filter logs for critical events
shell: |
cat /tmp/raw_logs/*.ndjson |
jq -c 'select(.ActionResult | IN("allow", "block")) |
select(.Event | IN("login", "auth_decision")) |
{timestamp: .CreatedAt,
user: .UserEmail,
ip_address: .IPAddress,
event_type: .Event,
result: .ActionResult,
raw_event: .}' > /tmp/filtered_events.json
register: filter_result

- name: Upload filtered batch to Secureframe Custom Evidence API
uri:
url: "https://secureframe.com/api/v2/custom-evidence"
method: POST
headers:
Authorization: "Bearer {{ secureframe_api_token }}"
Content-Type: "application/json"
body_format: json
body:
source: "cloudflare_zero_trust"
evidence_type: "access_control_logs"
records: "{{ lookup('file', '/tmp/filtered_events.json') | from_json }}"
```

### Step 3: Scheduling and Cleanup
The playbook runs daily via a systemd timer or a scheduled pipeline. Important to clean up processed logs and rotate them for cost control.

### Pitfalls & Notes
* **Rate Limiting:** Secureframe's API has rate limits. Batch your uploads and include retry logic in your script.
* **Field Mapping:** Ensure the `raw_event` field in your JSON contains the original data—auditors love that.
* **Token Security:** Store the Secureframe API token in a vault (like HashiCorp Vault). I used Ansible's `vault` lookup to decrypt it at runtime.
* **Verification:** Initially, pull a small sample and verify the evidence appears correctly in your Secureframe portal under the "Custom Evidence" tab.

This integration has significantly smoothed out our audit reviews, as we can now point to a timeline of specific access events pulled directly from our identity provider.

If you go this route, you'll likely need to adjust the jq filter for your specific use case—maybe you need different events or fields. Let me know if you hit any snags or have questions on the Terraform/Ansible bits. Happy to share more code snippets.

—John


Keep it simple.


   
Quote
(@marketing_ops_newbie_23)
Trusted Member
Joined: 4 months ago
Posts: 34
 

Thanks for sharing this! I'm still pretty new to stuff like this. When you say "continuous evidence for the Access Control requirements," do you mean you're automating the whole evidence collection so no one has to manually pull reports for an audit? That's a huge time saver if so.



   
ReplyQuote
(@crm_hopper)
Estimable Member
Joined: 4 months ago
Posts: 142
 

Yeah, that's exactly it. Manual report pulling is a huge audit time-sink, and it's error prone.

But here's the catch, automating it only helps if the logs are actually meaningful. I've seen setups where they collect mountains of "success" events but completely miss logging the "denied" attempts, which is the real evidence you need for access control. Garbage in, garbage out.


CRM is a necessary evil


   
ReplyQuote