docs(06-08): complete transition-address SSRF closure plan
Tasks completed: 1/1 - Decode and reclassify embedded IPv4 at the dial-time SSRF boundary SUMMARY: .planning/phases/06-http-routing-auth-groups-and-rate-limiting/06-08-SUMMARY.md
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
---
|
||||
phase: 06-http-routing-auth-groups-and-rate-limiting
|
||||
plan: 08
|
||||
subsystem: security
|
||||
tags: [ssrf, nat64, 6to4, rfc6052, rfc3056, netip]
|
||||
|
||||
requires:
|
||||
- phase: 06-http-routing-auth-groups-and-rate-limiting
|
||||
provides: dial-time fetchguard classification and the private/reserved IPv4 policy from plan 06-04
|
||||
provides:
|
||||
- NAT64 well-known and local-use embedded IPv4 classification
|
||||
- 6to4 embedded IPv4 classification
|
||||
- Dial-control regressions for transition-address SSRF rejection
|
||||
affects:
|
||||
- phase-12-manual-cover-url
|
||||
- phase-14-discogs-cover-import
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- Transition IPv6 formats are decoded into netip.AddrFrom4 and reclassified through the ordinary IPv4 policy
|
||||
- Recognized malformed RFC 6052 local-use addresses fail closed
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- fetchguard/ip.go
|
||||
- fetchguard/ip_test.go
|
||||
- fetchguard/fetch_test.go
|
||||
|
||||
key-decisions:
|
||||
- "isReservedOrPrivate owns Addr.Unmap and recursively applies the ordinary IPv4 table to supported transition embeddings so direct and dial-time callers share one policy"
|
||||
- "A 64:ff9b:1::/48 address with a non-zero RFC 6052 u octet is recognized as malformed and rejected rather than treated as public native IPv6"
|
||||
|
||||
patterns-established:
|
||||
- "Transition extraction recognizes only 64:ff9b::/96, 64:ff9b:1::/48, and 2002::/16; public embedded IPv4 remains allowed"
|
||||
|
||||
requirements-completed: [HTTP-07]
|
||||
|
||||
duration: 1h 20m
|
||||
completed: 2026-09-20
|
||||
---
|
||||
|
||||
# Phase 6 Plan 08: Transition-address SSRF closure Summary
|
||||
|
||||
**Dial-time NAT64 and 6to4 decoding now routes embedded IPv4 through the existing private/reserved policy while preserving public transition destinations**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 1h 20m
|
||||
- **Started:** 2026-09-20T15:13:49Z
|
||||
- **Completed:** 2026-09-20T16:34:04Z
|
||||
- **Tasks:** 1
|
||||
- **Files modified:** 3
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Closed the transition-address SSRF bypass for loopback, RFC1918, and link-local metadata IPv4 embedded in NAT64 well-known, NAT64 local-use, and 6to4 addresses.
|
||||
- Preserved public routing semantics by proving an embedded `8.8.8.8` remains public in all three supported formats.
|
||||
- Exercised the production `dialControl` boundary for every unsafe transition category and verified errors map to `ReasonPrivateIP` before a connection is attempted.
|
||||
- Moved IPv4-mapped normalization into the classifier so callers cannot accidentally bypass the IPv4 policy by omitting `Addr.Unmap`.
|
||||
|
||||
## Task Commits
|
||||
|
||||
The TDD task was committed atomically as RED then GREEN:
|
||||
|
||||
1. **Task 1 RED: Transition-address security regressions** - `1cd76fc` (test)
|
||||
2. **Task 1 GREEN: Transition-aware IP classification** - `99fa932` (fix)
|
||||
|
||||
**Plan metadata:** (this commit) docs(06-08): complete transition-address SSRF closure plan
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `fetchguard/ip.go` - Normalizes mapped IPv4, extracts IPv4 from the two NAT64 prefixes and 6to4, and fails closed on a malformed `/48` `u` octet.
|
||||
- `fetchguard/ip_test.go` - Covers loopback, RFC1918, metadata, and public embeddings across all three formats plus malformed local-use NAT64.
|
||||
- `fetchguard/fetch_test.go` - Calls production `dialControl` with unsafe bracketed IPv6 dial addresses and verifies sentinel/reason mapping.
|
||||
|
||||
## Decisions Made
|
||||
|
||||
- Put `Addr.Unmap` inside `isReservedOrPrivate` so helper callers and the dial hook cannot diverge on IPv4-mapped handling.
|
||||
- Reuse the existing IPv4 CIDR table recursively after transition extraction instead of creating a second transition-specific unsafe list.
|
||||
- Treat a non-zero RFC 6052 `u` octet as recognized-but-invalid, which causes the existing invalid-address path to fail closed.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None - plan executed exactly as written.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
- The sandboxed default Go build cache was read-only; verification used `GOCACHE=/tmp/summercms-go-build` without changing repository configuration.
|
||||
- Repository tests that create local `httptest` listeners required the existing elevated `go test` permission; the full package and repository suites passed once local sockets were allowed.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None - no external service configuration required.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- HTTP-07's transition-address gap is closed and the fetchguard boundary is ready for its Phase 12 and Phase 14 production callers.
|
||||
- Ready for plan 06-09 to close the partial-write panic recovery gap.
|
||||
|
||||
## TDD Gate Compliance
|
||||
|
||||
- RED: `1cd76fc` added failing helper and dial-control regressions before implementation.
|
||||
- GREEN: `99fa932` added transition extraction and made the regressions pass.
|
||||
- No refactor commit was needed.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- FOUND: `fetchguard/ip.go`
|
||||
- FOUND: `fetchguard/ip_test.go`
|
||||
- FOUND: `fetchguard/fetch_test.go`
|
||||
- FOUND: `1cd76fc`
|
||||
- FOUND: `99fa932`
|
||||
- `go test ./fetchguard -run 'Test(IsReservedOrPrivate|.*Transition|.*NAT64|.*6to4)' -count=1 -race -short` passed.
|
||||
- `go vet ./fetchguard` and `go test ./fetchguard -count=1 -race -short` passed.
|
||||
- `go vet ./...` and `go test ./... -short` passed.
|
||||
|
||||
---
|
||||
*Phase: 06-http-routing-auth-groups-and-rate-limiting*
|
||||
*Completed: 2026-09-20*
|
||||
Reference in New Issue
Block a user