From fb3273b03caabe6f129c6c362bb6ca01f7de3947 Mon Sep 17 00:00:00 2001 From: Andrew Meier Date: Tue, 24 Mar 2026 10:08:51 -0400 Subject: [PATCH 1/3] MEIER-327: Add WAF protection and Docker GHA build caching --- pulumi/src/cloudflare/index.ts | 1 + pulumi/src/cloudflare/waf.ts | 70 ++++++++++++++++++++++++++++++++++ pulumi/src/docker/image.ts | 4 ++ 3 files changed, 75 insertions(+) create mode 100644 pulumi/src/cloudflare/waf.ts diff --git a/pulumi/src/cloudflare/index.ts b/pulumi/src/cloudflare/index.ts index 366dee0..c5d2d61 100644 --- a/pulumi/src/cloudflare/index.ts +++ b/pulumi/src/cloudflare/index.ts @@ -1,3 +1,4 @@ import './provider' import './tunnel' import './record' +import './waf' diff --git a/pulumi/src/cloudflare/waf.ts b/pulumi/src/cloudflare/waf.ts new file mode 100644 index 0000000..ad3eefa --- /dev/null +++ b/pulumi/src/cloudflare/waf.ts @@ -0,0 +1,70 @@ +import * as cloudflare from '@pulumi/cloudflare' +import { provider } from './provider' +import * as config from '../config' + +const zone = cloudflare.getZoneOutput({ + filter: { + name: config.cloudflareConfig.zoneName, + account: { + id: config.cloudflareConfig.accountId + } + } +}, { provider }) + +const expression = [ + // Sensitive dotfiles and directories + '(http.request.uri.path contains "/.env")', + '(http.request.uri.path contains "/.git")', + '(http.request.uri.path contains "/.aws")', + '(http.request.uri.path contains "/.ssh")', + '(http.request.uri.path contains "/.terraform")', + + // CMS and framework probes + '(http.request.uri.path contains "/wp-")', + '(http.request.uri.path contains "/wordpress")', + '(http.request.uri.path contains "/xmlrpc")', + '(http.request.uri.path contains "/phpMyAdmin")', + '(http.request.uri.path contains "/phpmyadmin")', + '(http.request.uri.path contains "/pma")', + + // Admin and server management + '(http.request.uri.path contains "/admin")', + '(http.request.uri.path contains "/cgi-bin")', + '(http.request.uri.path contains "/actuator")', + '(http.request.uri.path contains "/solr")', + '(http.request.uri.path contains "/telescope")', + '(http.request.uri.path contains "/vendor")', + '(http.request.uri.path contains "/invoker")', + '(http.request.uri.path contains "/balancer-manager")', + + // Credential and config probes + '(http.request.uri.path contains "/credentials")', + '(http.request.uri.path contains "/known_hosts")', + '(http.request.uri.path contains "sendgrid")', + '(http.request.uri.path contains "codecommit")', + '(http.request.uri.path contains "/env.cfg")', + + // Dangerous file extensions + '(http.request.uri.path contains ".php")', + '(http.request.uri.path contains ".asp")', + '(http.request.uri.path contains ".jsp")', + '(http.request.uri.path contains ".cgi")', + '(http.request.uri.path contains ".yml")', + '(http.request.uri.path contains ".xml")', + '(http.request.uri.path contains ".bak")', + '(http.request.uri.path contains ".rb")', +].join(' or ') + +new cloudflare.Ruleset(`${config.identifier}-waf`, { + zoneId: zone.zoneId, + name: 'Block vulnerability scanners', + kind: 'zone', + phase: 'http_request_firewall_custom', + rules: [{ + ref: 'block_scan_probes', + description: 'Block common vulnerability scanner paths and file extensions', + enabled: true, + expression, + action: 'block', + }] +}, { provider }) diff --git a/pulumi/src/docker/image.ts b/pulumi/src/docker/image.ts index 29928e4..705808e 100644 --- a/pulumi/src/docker/image.ts +++ b/pulumi/src/docker/image.ts @@ -7,6 +7,8 @@ import * as config from '../config' const registryUri = config.dockerConfig.registryUri const registryHost = registryUri.split('/')[0] +const isGitHubActions = !!process.env.GITHUB_ACTIONS + export const image = new dockerBuild.Image(config.identifier, { tags: [ pulumi.interpolate`${registryUri}/${config.identifier}` @@ -23,6 +25,8 @@ export const image = new dockerBuild.Image(config.identifier, { username: 'oauth2accesstoken', password: config.dockerConfig.registryAccessToken, }], + cacheFrom: isGitHubActions ? [{ gha: {} }] : [], + cacheTo: isGitHubActions ? [{ gha: { mode: dockerBuild.CacheMode.Max, ignoreError: true } }] : [], }, { provider }) export const imageRef = image.ref From 27c4508019646dc8f109b78f901b07c4aac00c0f Mon Sep 17 00:00:00 2001 From: Andrew Meier Date: Tue, 24 Mar 2026 10:13:09 -0400 Subject: [PATCH 2/3] MEIER-327: Extract shared zone lookup into zone.ts --- pulumi/src/cloudflare/index.ts | 1 + pulumi/src/cloudflare/record.ts | 10 +--------- pulumi/src/cloudflare/waf.ts | 10 +--------- pulumi/src/cloudflare/zone.ts | 12 ++++++++++++ 4 files changed, 15 insertions(+), 18 deletions(-) create mode 100644 pulumi/src/cloudflare/zone.ts diff --git a/pulumi/src/cloudflare/index.ts b/pulumi/src/cloudflare/index.ts index c5d2d61..b417bb1 100644 --- a/pulumi/src/cloudflare/index.ts +++ b/pulumi/src/cloudflare/index.ts @@ -1,4 +1,5 @@ import './provider' +import './zone' import './tunnel' import './record' import './waf' diff --git a/pulumi/src/cloudflare/record.ts b/pulumi/src/cloudflare/record.ts index 1004773..19b08f3 100644 --- a/pulumi/src/cloudflare/record.ts +++ b/pulumi/src/cloudflare/record.ts @@ -2,15 +2,7 @@ import * as cloudflare from '@pulumi/cloudflare' import * as config from '../config' import * as tunnel from './tunnel' import { provider } from './provider' - -const zone = cloudflare.getZoneOutput({ - filter: { - name: config.cloudflareConfig.zoneName, - account: { - id: config.cloudflareConfig.accountId - } - } -}, { provider }) +import { zone } from './zone' new cloudflare.DnsRecord(config.identifier, { zoneId: zone.zoneId, diff --git a/pulumi/src/cloudflare/waf.ts b/pulumi/src/cloudflare/waf.ts index ad3eefa..48d503c 100644 --- a/pulumi/src/cloudflare/waf.ts +++ b/pulumi/src/cloudflare/waf.ts @@ -1,15 +1,7 @@ import * as cloudflare from '@pulumi/cloudflare' import { provider } from './provider' import * as config from '../config' - -const zone = cloudflare.getZoneOutput({ - filter: { - name: config.cloudflareConfig.zoneName, - account: { - id: config.cloudflareConfig.accountId - } - } -}, { provider }) +import { zone } from './zone' const expression = [ // Sensitive dotfiles and directories diff --git a/pulumi/src/cloudflare/zone.ts b/pulumi/src/cloudflare/zone.ts new file mode 100644 index 0000000..b0fe5d2 --- /dev/null +++ b/pulumi/src/cloudflare/zone.ts @@ -0,0 +1,12 @@ +import * as cloudflare from '@pulumi/cloudflare' +import { provider } from './provider' +import * as config from '../config' + +export const zone = cloudflare.getZoneOutput({ + filter: { + name: config.cloudflareConfig.zoneName, + account: { + id: config.cloudflareConfig.accountId + } + } +}, { provider }) From aed8e66f05b2df1c80d23d25fda88032d81301b3 Mon Sep 17 00:00:00 2001 From: Andrew Meier Date: Tue, 24 Mar 2026 11:11:20 -0400 Subject: [PATCH 3/3] MEIER-327: Point tunnel config to localhost for sidecar cloudflared --- pulumi/src/cloudflare/tunnel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pulumi/src/cloudflare/tunnel.ts b/pulumi/src/cloudflare/tunnel.ts index 8ded6ed..f8c418b 100644 --- a/pulumi/src/cloudflare/tunnel.ts +++ b/pulumi/src/cloudflare/tunnel.ts @@ -16,7 +16,7 @@ new cloudflare.ZeroTrustTunnelCloudflaredConfig(config.identifier, { ingresses: [ { hostname: `${config.identifier}.${config.cloudflareConfig.zoneName}`, - service: `http://${config.identifier}.${config.k8sConfig.namespace}.svc.cluster.local:80` + service: 'http://localhost:80' }, { service: 'http_status:404'