From 7ab5c77dc02d98d841694dc2ba4252931d7ae3a7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 5 Sep 2026 15:38:03 +0000 Subject: [PATCH 1/3] Initial plan From 14a54cc91837c04ff250d5d84414ae098fae7285 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 5 Sep 2026 15:46:17 +0000 Subject: [PATCH 2/3] fix: reduce Squid negative DNS TTL to avoid caching SERVFAIL --- samples/audit/squid.conf | 2 ++ src/squid/config-sections.test.ts | 12 +++++++++++- src/squid/config-sections.ts | 15 ++++++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/samples/audit/squid.conf b/samples/audit/squid.conf index 42551a9d7..204637d28 100644 --- a/samples/audit/squid.conf +++ b/samples/audit/squid.conf @@ -74,6 +74,8 @@ cache deny all # DNS settings - Squid resolves all domains for HTTP/HTTPS traffic dns_nameservers 8.8.8.8 8.8.4.4 +negative_dns_ttl 1 seconds +dns_timeout 5 seconds # Forwarded headers forwarded_for delete diff --git a/src/squid/config-sections.test.ts b/src/squid/config-sections.test.ts index 137d0bac8..314548311 100644 --- a/src/squid/config-sections.test.ts +++ b/src/squid/config-sections.test.ts @@ -186,7 +186,17 @@ describe('buildConfigSections', () => { it('uses custom DNS servers when provided', () => { const { dnsSection } = buildWithDefaults({ dnsServers: ['1.1.1.1', '1.0.0.1'] }); -expect(dnsSection).toBe('dns_nameservers 1.1.1.1 1.0.0.1'); + expect(dnsSection).toMatch(/^dns_nameservers 1\.1\.1\.1 1\.0\.0\.1$/m); + }); + + it('shrinks negative_dns_ttl to avoid caching a single transient SERVFAIL', () => { + const { dnsSection } = buildWithDefaults(); + expect(dnsSection).toMatch(/^negative_dns_ttl 1 seconds$/m); + }); + + it('lowers dns_timeout so a stalled query fails fast', () => { + const { dnsSection } = buildWithDefaults(); + expect(dnsSection).toMatch(/^dns_timeout 5 seconds$/m); }); }); diff --git a/src/squid/config-sections.ts b/src/squid/config-sections.ts index d4e000590..729ac1281 100644 --- a/src/squid/config-sections.ts +++ b/src/squid/config-sections.ts @@ -201,7 +201,20 @@ function generateAllowedIpSection(domains: string[]): string { } function generateDnsSection(dnsServers?: string[]): string { - return `dns_nameservers ${(dnsServers && dnsServers.length > 0) ? dnsServers.join(' ') : DEFAULT_DNS_SERVERS.join(' ')}`; + const servers = (dnsServers && dnsServers.length > 0) ? dnsServers.join(' ') : DEFAULT_DNS_SERVERS.join(' '); + return `dns_nameservers ${servers} +# A single transient upstream DNS failure (e.g. a SERVFAIL from an overloaded +# resolver during concurrent container startup) must not turn into a sustained +# false-positive block of an allowlisted domain. Squid's default +# negative_dns_ttl (1 minute) caches that one failure and rejects every +# request for the same domain for up to 60 seconds. Shrinking it means the +# very next lookup attempt re-queries the resolver instead of replaying the +# cached failure. +negative_dns_ttl 1 seconds +# Fail a stalled DNS query quickly (default dns_timeout is 30 seconds) so a +# slow/unresponsive nameserver doesn't stall requests for that long before +# Squid tries the next configured nameserver. +dns_timeout 5 seconds`; } function generateConfigSections(options: { From 100a90c4ad55e59b5f88123d6d2cfd649150fbd6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 5 Sep 2026 17:03:38 +0000 Subject: [PATCH 3/3] fix: preserve Squid DNS failover retries --- samples/audit/squid.conf | 3 +- src/squid/config-sections.test.ts | 46 +++++++++++++++++++++++++++++-- src/squid/config-sections.ts | 10 ++++--- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/samples/audit/squid.conf b/samples/audit/squid.conf index 204637d28..a4955270e 100644 --- a/samples/audit/squid.conf +++ b/samples/audit/squid.conf @@ -75,7 +75,8 @@ cache deny all # DNS settings - Squid resolves all domains for HTTP/HTTPS traffic dns_nameservers 8.8.8.8 8.8.4.4 negative_dns_ttl 1 seconds -dns_timeout 5 seconds +dns_retransmit_interval 1 seconds +dns_timeout 10 seconds # Forwarded headers forwarded_for delete diff --git a/src/squid/config-sections.test.ts b/src/squid/config-sections.test.ts index 314548311..45e094a0f 100644 --- a/src/squid/config-sections.test.ts +++ b/src/squid/config-sections.test.ts @@ -13,6 +13,34 @@ function buildWithDefaults(overrides: Partial line.startsWith(`${directive} `)); + const value = directiveLine?.split(/\s+/)[1]; + if (!value) { + throw new Error(`Missing ${directive} directive`); + } + return Number(value); +} + +function canReachFallbackResolver(options: { + dnsRetransmitIntervalSeconds: number; + dnsTimeoutSeconds: number; + resolverOutcomes: ('stall' | 'success')[]; +}): boolean { + const { dnsRetransmitIntervalSeconds, dnsTimeoutSeconds, resolverOutcomes } = options; + let elapsedSeconds = 0; + for (const resolverOutcome of resolverOutcomes) { + if (elapsedSeconds >= dnsTimeoutSeconds) { + return false; + } + if (resolverOutcome === 'success') { + return true; + } + elapsedSeconds += dnsRetransmitIntervalSeconds; + } + return false; +} + describe('buildConfigSections', () => { describe('portConfig', () => { it('emits http_port with the configured port', () => { @@ -194,9 +222,23 @@ describe('buildConfigSections', () => { expect(dnsSection).toMatch(/^negative_dns_ttl 1 seconds$/m); }); - it('lowers dns_timeout so a stalled query fails fast', () => { + it('uses a short retransmit interval with enough total timeout for resolver fallback', () => { + const { dnsSection } = buildWithDefaults(); + expect(dnsSection).toMatch(/^dns_retransmit_interval 1 seconds$/m); + expect(dnsSection).toMatch(/^dns_timeout 10 seconds$/m); + }); + + it('keeps DNS timeout above retransmit interval so fallback nameservers are queried', () => { const { dnsSection } = buildWithDefaults(); - expect(dnsSection).toMatch(/^dns_timeout 5 seconds$/m); + const dnsRetransmitIntervalSeconds = parseSecondsDirective(dnsSection, 'dns_retransmit_interval'); + const dnsTimeoutSeconds = parseSecondsDirective(dnsSection, 'dns_timeout'); + + expect(dnsRetransmitIntervalSeconds).toBeLessThan(dnsTimeoutSeconds); + expect(canReachFallbackResolver({ + dnsRetransmitIntervalSeconds, + dnsTimeoutSeconds, + resolverOutcomes: ['stall', 'success'], + })).toBe(true); }); }); diff --git a/src/squid/config-sections.ts b/src/squid/config-sections.ts index 729ac1281..e45bda299 100644 --- a/src/squid/config-sections.ts +++ b/src/squid/config-sections.ts @@ -211,10 +211,12 @@ function generateDnsSection(dnsServers?: string[]): string { # very next lookup attempt re-queries the resolver instead of replaying the # cached failure. negative_dns_ttl 1 seconds -# Fail a stalled DNS query quickly (default dns_timeout is 30 seconds) so a -# slow/unresponsive nameserver doesn't stall requests for that long before -# Squid tries the next configured nameserver. -dns_timeout 5 seconds`; +# Retry another configured nameserver quickly while keeping the total timeout +# above the retry interval. If dns_timeout is equal to Squid's default +# dns_retransmit_interval (5 seconds), Squid can hit the total timeout before +# sending the retry to the fallback nameserver. +dns_retransmit_interval 1 seconds +dns_timeout 10 seconds`; } function generateConfigSections(options: {