Skip to content

fix(dns): do not cache answers with a zero TTL - #15019

Open
jmrplens wants to merge 2 commits into
Kong:masterfrom
jmrplens:fix/dns-zero-ttl
Open

jmrplens wants to merge 2 commits into
Kong:masterfrom
jmrplens:fix/dns-zero-ttl

Conversation

@jmrplens

@jmrplens jmrplens commented Sep 24, 2026 •

Copy link
Copy Markdown

Summary

A DNS answer with a TTL of 0 means the answer may only be used for the transaction in progress and must not be cached (RFC 1035 section 3.2.1). The new DNS client passes that zero straight through to mlcache, where a zero TTL is the sentinel for "never expires": set_lru drops the TTL (if ttl == 0 then ttl = nil end) and the shm path marks the entry with NO_TTL_FLAG. One answer with TTL 0, which a caching resolver returns routinely, therefore pins the name in kong_dns_cache for the life of the node, across every worker and across kong reload, and Kong keeps sending traffic to addresses that have changed. This is what #15000 reports against an AWS ALB, where the pinned addresses were withdrawn and the requests turned into 504s.

The fix returns such answers with a negative TTL, which is the convention resolve_callback already uses for cache_only, so mlcache skips both of its caches and the name is resolved again on the next request. Every other TTL keeps its value, which is the difference from the resolver_valid_ttl workaround: that one overrides the TTL of every answer, so it also stops honouring the zone for names that publish a perfectly good TTL.

The legacy client does not have this failure mode because it never hands a bare zero to a cache that reads zero as forever: it inserts with lru_ttl = ttl + staleTtl, so a zero-TTL record lands already stale and gets refreshed.

How I verified it. I could not run the busted suite in my environment (no busted rock available for the Lua version in the container I had), so I exercised the same code path with a standalone resty harness that mocks resty.dns.resolver.query, resolves a name whose record carries ttl = 0, replaces the mocked record with a different address, and resolves again:

tree first resolve second resolve queries
master 1.2.3.4 1.2.3.4 1
this branch 1.2.3.4 5.6.7.8 2

The test added to spec/01-unit/30-new-dns-client/03-old_client_cache_spec.lua asserts exactly that, and it is the assertion on the number of queries that fails on master.

Checklist

  • The Pull Request has tests
  • A changelog file has been created under changelog/unreleased/kong or skip-changelog label added on PR if changelog is unnecessary. README.md
  • There is a user-facing docs PR against https://github.com/Kong/developer.konghq.com - not needed, this restores the documented behaviour of the TTL rather than changing any configuration

Issue reference

Discussion: #15000

Closes #15021

A TTL of 0 means the answer may only be used for the transaction in
progress and must not be cached (RFC 1035 section 3.2.1). The new DNS
client passed that zero straight to mlcache, which reads a zero TTL as
"never expires": it drops the TTL for the LRU and marks the shm entry
with NO_TTL_FLAG. A single answer with TTL 0, which a caching resolver
returns routinely, therefore pinned the name in kong_dns_cache for the
life of the node, and Kong kept sending traffic to addresses that had
changed.

Return such answers with a negative TTL, the convention resolve_callback
already uses for cache_only, so mlcache skips both of its caches and the
name is resolved again on the next request. Every other TTL is
unaffected, unlike the resolver_valid_ttl workaround, which overrides
the TTL of every answer.

* add cache_ttl() and use it on both return paths of
  resolve_query_names()
* add a regression test to the new DNS client cache spec
@CLAassistant

CLAassistant commented Sep 24, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New DNS client permanently pins a DNS record received with a TTL of 0, which can happen when querying a caching resolver.

2 participants