Class: CloudflareIpListSyncWorker

Inherits:
Object
  • Object
show all
Includes:
Sidekiq::Job
Defined in:
app/workers/cloudflare_ip_list_sync_worker.rb

Overview

Hourly sync of CRM employee sign-in IPs to the Cloudflare $warmlyyours_users IP list.

The "Always Allow WY Users" Cloudflare WAF rule skips security checks for IPs in this
list. Without it, employees outside US/CA (e.g. India via IPv6) hit SBFM challenges
and managed WAF checks on every CRM page load, causing Turbo Frame navigation stalls.

Uses PUT (full replacement) so stale IPs naturally drop when employees haven't signed
in within 15 days (matching IpDetector's base_scope window).

Runs hourly on the hour via sidekiq-cron, plus on-demand after employee
sign-in via Events::EmployeeSignedIn. Idempotent — safe to re-run.

Constant Summary collapse

SKIPPABLE_ERROR =

Cloudflare failures that must not page an hourly, idempotent sync (AppSignal
#5300). entitlements.not_available is permanent account-plan state. An
HTTP 5xx is Cloudflare's own edge failing (error code: 522) and returning
a non-JSON body — the blip outlasts all 4 Sidekiq attempts (~2 min) but the
next hourly run heals it. Everything else still raises so retry +
sidekiq_retries_exhausted do their job.

/entitlements\.not_available|HTTP 5\d\d/

Instance Method Summary collapse

Instance Method Details

#performObject

Runs the job.

Returns:

  • (Object)

    the result



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'app/workers/cloudflare_ip_list_sync_worker.rb', line 38

def perform
  items = IpDetector.instance.cloudflare_ip_list_items

  if items.empty?
    Rails.logger.warn "[CloudflareIpListSyncWorker] No IPs to sync — skipping"
    return
  end

  service = CloudflareRulesService.instance
  list_id = service.find_list_id_by_name(CloudflareRulesService::WARMLYYOURS_USERS_LIST_NAME)
  result  = service.replace_ip_list_items(list_id, items)

  raise "Cloudflare IP list sync failed: #{result[:error]}" if result.is_a?(Hash) && result[:error]

  Rails.logger.info "[CloudflareIpListSyncWorker] Synced #{items.size} IPs to $warmlyyours_users (list #{list_id})"
rescue RuntimeError => e
  # Catches both failure paths — find_list_id_by_name raises directly, while
  # replace_ip_list_items returns an error Hash we re-raise above.
  raise unless SKIPPABLE_ERROR.match?(e.message)

  Rails.logger.warn "[CloudflareIpListSyncWorker] Skipping sync: #{e.message}"
end