Needle in a Haystack: lighting an entire IPv6 /64 with clear signals

Needle in a Haystack: lighting an entire IPv6 /64 with clear signals

Tech-scroll 109

“Hide the needle by filling the stack with signal. Make the path for the work that matters, and let noise find only more noise.” — Tech-scroll 109

Breath Technology delivers service first and support before self. Ownership over rental, clarity over noise, and practical engineering over fashion. This article shares a defensive pattern that turns the size of IPv6 into an advantage while keeping real services calm and private.


The idea in one paragraph

Light a large IPv6 slice so every address appears alive and replies with honest, consistent signals. Ping works for reachability. SSH, HTTP, and HTTPS return administratively prohibited. All other TCP returns a fast close. UDP returns port unreachable. Real lanes remain separate and controlled. The result is a giant haystack with clean behaviour and no hidden hooks.

For context


A /50 lit range contains three hundred two sextillion, two hundred thirty-one quintillion, four hundred fifty-four quadrillion, nine hundred three trillion, six hundred fifty-seven billion, two hundred ninety-three million, six hundred seventy-six thousand, five hundred forty-four IPv6 addresses, and the DNS generator creates a name for every single address in that space. Find that needle in a haystack!


Why this helps

  • Scan pressure drops: automation sees the same clear answer everywhere rather than a patchwork of timeouts and half‑open states.
  • Origins stay private: longer routes and frontends keep production away from public address space.
  • Ownership: no rental of software, no lock‑in. The posture runs on your own systems and remains under your control.
  • Cost aware: one local route and a small ruleset handle a vast space with minimal overhead.

Live telemetry (sanitized)

A real packet seen during testing, shown with source prefix masked for privacy:
The destination resolves to https://breathtechnology.co.uk/ and as we started seeing this within an hour of DNS going live we wanted to find a way to protect networks, give the scanners something to find and really, they may one day give up when all they can do is scan and find more of the same filtered responses.

[ kernel ] nftables: DROP FORWARD : IN=ppp0 OUT=eth3 
SRC=2a06:48xx::/48  DST=2a02:8012:bc57:003a::4  PROTO=TCP SPT=36427 DPT=8097 FLAGS=SYN

Why sanitized? Publishing full source IPs can expose individuals or operators. Masking to a prefix (e.g., /48 or /64), rounding timestamps, and summarising by ASN and country gives context without singling anyone out. The haystack posture aims to reduce harm and noise, not to shame scanners.


How it works (at a glance)

1) Routing

Treat a wide IPv6 prefix as local on the defender. Example placeholders only:

honeydns:/etc/sysctl.d# cat 99-breath.conf
net.ipv6.ip_nonlocal_bind = 1
# treat the lit slice as local (example prefix)
ip -6 route add local 2a02:8012:bc57:c000::/50 dev lo

Real services travel on longer routes: add exact /128 or whole /64 lanes upstream so production never touches the hay.

2) Signals

Use a short nftables set to provide honest responses:

# concept only — values redacted
# 22/80/443 → administratively prohibited
# other TCP → closed (RST)
# UDP → port unreachable
#!/usr/sbin/nft -f

flush ruleset

define HONEY = { 2a02:8012:bc57:c000::/50 }

table inet filter {
  chain input {
    iifname {"wan0"} tcp dport 53 accept
    # log + signal for 22/80/443  -> filtered (admin-prohibited)
    # Honey range signals + logs
    ip6 daddr $HONEY tcp dport {22,80,443} \
    log prefix "nftable: needle in a haystack filtered ports " \
    flags all  reject with icmpx type admin-prohibited

    meta l4proto tcp ip6 daddr $HONEY \
    log prefix "nftable: needle in a haystack tcp closed " \
    flags all reject with tcp reset
    
    meta l4proto udp ip6 daddr $HONEY \
    log prefix "nftable: needle in a haystack udp closed " \
    flags all reject with icmpx port-unreachable
    }
}

Echo and control ICMPv6 remain open for health and path MTU.

3) Names

Run a tiny authoritative that synthesises AAAA inside a lit /64.
Delegation from the parent zone provides a single NS with IPv6 glue.
Round‑robin or hashing creates countless stable names without a giant zone file.


What a scanner sees

  • Host up on any address in the lit range.
  • 22/80/443: administratively prohibited (reads as filtered).
  • Other TCP: quick RST (closed).
  • UDP: clean port unreachable.
    No breadcrumbs to real origins, no theatrical tricks.

What keeps the real lanes safe

  • More‑specific routes steer known services away from the hay.
  • Fronts only receive public names; origins remain nameless in external zones.
  • Quiet reverse: generic PTR for the hay, explicit PTR only for true hosts.
  • Light logging: kernel or NFLOG to structured storage, ready for study without noise.
  • DNS resilience: IPv6 NS with TCP and UDP open; a second NS for redundancy when desired.

Ethics and scope

No deception that harms users or peers. No entrapment. Clear signals reduce wasted effort for both sides and raise resilience without theatre.


Where this fits

  • Estates that value sovereignty and control.
  • Teams that prefer private cloud and locally hosted platforms while still using public cloud with intent.
  • Organisations that want WordPress care, small webapps, AI content workflows, and infrastructure that remains theirs.

Based in Hampshire, serving the UK. We create, manage, and support infrastructure with a steady hand. Service comes first. Support comes before self.


Want the pattern?

A short engagement delivers:

  1. A lit IPv6 slice sized to need.
  2. Routes that guard real lanes.
  3. Names that resolve at scale without exposing origins.
  4. Logging and dashboards that show real activity across the hay.

Enquiries: reach out through Breath Technology. A deeper technical note with code examples can follow, with addresses redacted and placeholders only.

Is this real?

Yes. This has been built and exercised on the wire. The authoritative generator answers deterministically for the delegated zone and can provide a stable name for every single /128 inside the lit /50 — that is, three hundred two sextillion, two hundred thirty‑one quintillion, four hundred fifty‑four quadrillion, nine hundred three trillion, six hundred fifty‑seven billion, two hundred ninety‑three million, six hundred seventy‑six thousand, five hundred forty‑four (302,231,454,903,657,293,676,544) addresses — without storing a giant zone file.

It runs lean. While serving public queries the system looked like this: DNS Names and addresses to protect the innocent, this is real would you like the server that does this?

How do you start such a service?

It's really quite simple, however all is not quite as it seems, our code lights a /64 DNS address space as we will see when you scroll down the page.

route:/home/director/honeyDNS# /usr/local/bin/honeydns \
  -zone honeyilitupthenetwork.zerodns.co.uk. \
  -prefix 2a02:8012:bc57:c000::/50 \
  -nsname ns.honeyilitupthenetwork.zerodns.co.uk. \
  -nsaaaa 2a02:8012:bc57:3::1 \
  -listen "[2a02:8012:bc57:3::1]:53"

What happens when the service is started manually?

2025/08/15 07:09:13 zone=honeyilitupthenetwork.zerodns.co.uk. prefix=2a02:8012:bc57:c000::/50 listen=[2a02:8012:bc57:3::1]:53 ns=ns.honeyilitupthenetwork.zerodns.co.uk.(2a02:8012:bc57:3::1) serial=2508150636 full=15082025060913

Proof it works

honeydns:/home/director/honeyDNS# ping host-57005.honeyilitupthenetwork.zerodns.co.uk
PING host-57005.honeyilitupthenetwork.zerodns.co.uk (2a02:8012:bc57:c000::dead): 56 data bytes
64 bytes from 2a02:8012:bc57:c000::dead: seq=0 ttl=64 time=0.038 ms
64 bytes from 2a02:8012:bc57:c000::dead: seq=1 ttl=64 time=0.036 ms

Is it real? as in does it resolve?

honeydns:/home/director/honeyDNS# dig AAAA ns.honeyilitupthenetwork.zerodns.co.uk @2a02:8012:bc57:3::1 +norec
dig AAAA host-57005.honeyilitupthenetwork.zerodns.co.uk @1.1.1.1
dig AAAA host-57005.honeyilitupthenetwork.zerodns.co.uk @8.8.8.8

; <<>> DiG 9.20.11 <<>> AAAA ns.honeyilitupthenetwork.zerodns.co.uk @2a02:8012:bc57:3::1 +norec
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1793
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;ns.honeyilitupthenetwork.zerodns.co.uk.        IN AAAA

;; ANSWER SECTION:
ns.honeyilitupthenetwork.zerodns.co.uk. 300 IN AAAA 2a02:8012:bc57:3::1

;; Query time: 0 msec
;; SERVER: 2a02:8012:bc57:3::1#53(2a02:8012:bc57:3::1) (UDP)
;; WHEN: Fri Aug 15 07:11:30 BST 2025
;; MSG SIZE  rcvd: 122


; <<>> DiG 9.20.11 <<>> AAAA host-57005.honeyilitupthenetwork.zerodns.co.uk @1.1.1.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56292
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; EDE: 23 (Network Error): (13.41.102.86:53 rcode=REFUSED for honeyilitupthenetwork.zerodns.co.uk A)
;; QUESTION SECTION:
;host-57005.honeyilitupthenetwork.zerodns.co.uk.        IN AAAA

;; ANSWER SECTION:
host-57005.honeyilitupthenetwork.zerodns.co.uk. 300 IN AAAA 2a02:8012:bc57:c000::dead

;; Query time: 22 msec
;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
;; WHEN: Fri Aug 15 07:11:30 BST 2025
;; MSG SIZE  rcvd: 180


; <<>> DiG 9.20.11 <<>> AAAA host-57005.honeyilitupthenetwork.zerodns.co.uk @8.8.8.8
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61179
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;host-57005.honeyilitupthenetwork.zerodns.co.uk.        IN AAAA

;; ANSWER SECTION:
host-57005.honeyilitupthenetwork.zerodns.co.uk. 300 IN AAAA 2a02:8012:bc57:c000::dead

;; Query time: 17 msec
;; SERVER: 8.8.8.8#53(8.8.8.8) (UDP)
;; WHEN: Fri Aug 15 07:11:30 BST 2025
;; MSG SIZE  rcvd: 103

How many addresses are there really?

Well that is a great question, per /64 network instance it is 1844,6744,0737,0955,1615 DNS Names created, that's a pretty large number and its only one /64 network name set. Want more? Launch more instances for now.

honeydns:/home/director/honeyDNS# dig AAAA host-5700500.honeyilitupthenetwork.zerodns.co.uk @8.8.8.8

; <<>> DiG 9.20.11 <<>> AAAA host-5700500.honeyilitupthenetwork.zerodns.co.uk @8.8.8.8
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48239
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;host-5700500.honeyilitupthenetwork.zerodns.co.uk. IN AAAA

;; ANSWER SECTION:
host-5700500.honeyilitupthenetwork.zerodns.co.uk. 300 IN AAAA 2a02:8012:bc57:c000::56:fb94

;; Query time: 17 msec
;; SERVER: 8.8.8.8#53(8.8.8.8) (UDP)
;; WHEN: Fri Aug 15 07:12:46 BST 2025
;; MSG SIZE  rcvd: 105

Where do you stop when a whole /48 IPv6 network is alive or is it? now this is truly the needle in the haystack! and the computer cant quite cope so we have to make do with the limits of a /64 for today

route:/home/director/honeyDNS# dig AAAA host-18446744073709551615.honeyilitupthenetwork.zerodns.co.uk @8.8.8.8

; <<>> DiG 9.20.11 <<>> AAAA host-18446744073709551615.honeyilitupthenetwork.zerodns.co.uk @8.8.8.8
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47805
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;host-18446744073709551615.honeyilitupthenetwork.zerodns.co.uk. IN AAAA

;; ANSWER SECTION:
host-18446744073709551615.honeyilitupthenetwork.zerodns.co.uk. 300 IN AAAA 2a02:8012:bc57:c000:ffff:ffff:ffff:ffff

;; Query time: 17 msec
;; SERVER: 8.8.8.8#53(8.8.8.8) (UDP)
;; WHEN: Fri Aug 15 07:17:11 BST 2025
;; MSG SIZE  rcvd: 118

Addendum: /50 vs /64 — names, math, and why it stays put

The first pass lit a /50 at the router (a single local route), so any address inside that quarter of the /48 answers consistently. The DNS generator, however, is pinned to a /64 by design. It writes the low 64 bits of the address from the digits in the left label and never touches the upper 64 bits. That means:

  • Even absurdly long numbers wrap modulo 2⁶⁴, so answers cannot “escape” into the next /64. Example: host-18446744073709551615…::ffff:ffff:ffff:ffff.
  • Names keep resolving forever, yet the computed addresses stay inside the one chosen /64.

Why do this? Deterministic, bounded answers are safer and simpler to operate. The haystack stays huge, the names stay stable, and there is no risk of leaking into unintended subnets.

Want names to roam across the whole /50 later? Switch to a two‑part label (e.g., host-<slice>-<id>) where <slice> selects one of the 16,384 /64s in the /50 and <id> sets the interface identifier. The generator can be extended to do that in a few lines.

Want only a /64 lit at the router as well? Narrow the local route (placeholders shown):

# remove the wide local route (if present)
ip -6 route del local 2a02:8012:bc57:c000::/50 dev lo || true

# light only one /64 
ip -6 route replace local 2a02:8012:bc57:c000::/64 dev lo

Keep the DNS generator’s -prefix set to that same /64. Names continue to resolve; the machine only answers inside that /64, exactly as intended.

Recursion is disabled (AA=1, RA=0); this server is authoritative-only.

Call to action

Do you want to hide your needles in a haystack? Get in contact with Breath Technology to learn more.

This pattern runs anywhere a small Go binary can run — Alpine Linux (the basis for this build), Arch Linux, any mainstream Linux, the BSDs (FreeBSD/OpenBSD), macOS, and Windows. No giant zone files, no databases, no rentals. Point the tool at a prefix and a zone, and it names every /128 in your chosen /64. Launch one haystack or launch many; each /64 can stand alone or be orchestrated as a fleet.

Ways to engage

  • Assessment & pilot: light a /64, verify signals, carve out real lanes.
  • Production rollout: delegation, routing, nftables, logging, and dashboards.
  • Workshop: hands-on session for teams on design, operations, and safeguards.

Service comes first and support before self. Ownership over rental. If resilient, owned infrastructure sounds right for your estate, Breath Technology is ready to help.