Skip to content

Add extractDirectClientIP for the client address that cannot be spoofed - #1219

Open
pjfanning wants to merge 1 commit into
apache:mainfrom
pjfanning:client-ip
Open

Add extractDirectClientIP for the client address that cannot be spoofed#1219
pjfanning wants to merge 1 commit into
apache:mainfrom
pjfanning:client-ip

Conversation

@pjfanning

Copy link
Copy Markdown
Member

Motivation

extractClientIP reads X-Forwarded-For, then X-Real-IP, and only then the AttributeKeys.remoteAddress attribute. Those headers are set by whoever sent the request, so unless a trusted proxy in front of the server overwrites them, the client decides which address the application sees. That is exactly what you want behind a proxy, but for uses where the address must not be under the client's control — access control, rate limiting, audit logs — there is no equally convenient directive, and reaching for the attribute means writing the extraction by hand.

The documentation page for extractClientIP does carry a warning about this, but the scaladoc and javadoc of the directive itself say nothing, which is where most people meet it.

Modification

  • Add extractDirectClientIP to both DSLs. It extracts the AttributeKeys.remoteAddress attribute alone and ignores forwarding headers, so a client cannot choose the value; behind a proxy it is the address of the proxy rather than of the client.
  • Mention the difference in the scaladoc and javadoc of extractClientIP and point at the new directive.
  • Give the new directive a documentation page with Scala and Java examples, list it in the misc-directives index, and cross-link it from the warning on the extractClientIP page.

extractClientIP itself is unchanged, and its fallback now simply reuses the new directive.

Result

The address that a client cannot spoof is available as a one-liner, and the distinction between the two directives is visible in the API docs rather than only on the documentation site.

Naming note: I went with extractDirectClientIP to say "the peer of this connection" without implying it is the end user's address. Happy to rename if you prefer something else.

Tests

  • sbt "http-tests/testOnly org.apache.pekko.http.scaladsl.server.directives.MiscDirectivesSpec" - pass; 4 new tests: the attribute is extracted, X-Forwarded-For and X-Real-IP sent by a client are ignored, Unknown when the attribute is not set, plus one for extractClientIP falling back to the attribute
  • sbt http-tests/test - pass (1489 tests)
  • sbt docs/test - pass, covering the new Scala and Java examples
  • sbt docs/paradox - pass; the new page renders with its signature and both examples
  • sbt +http/mimaReportBinaryIssues - pass on 2.13 and 3
  • sbt http/scalafmt http-tests/Test/scalafmt docs/Test/scalafmt docs/Test/javafmt - clean

References

None - makes the address that a client cannot spoof directly available

Motivation:
`extractClientIP` prefers the X-Forwarded-For and X-Real-IP headers over
the remote address attribute. Those headers are set by whoever sent the
request, so unless a trusted proxy in front of the server overwrites them
the client picks the address the application sees. That is the intended
behaviour behind a proxy, but there is no equally convenient directive for
the cases where the address must not be under the client's control, and the
scaladoc and javadoc of `extractClientIP` do not mention the difference at
all (only the docs page does).

Modification:
Add `extractDirectClientIP` to both DSLs, extracting the
`AttributeKeys.remoteAddress` attribute alone, and point at it from the
scaladoc and javadoc of `extractClientIP`. Document the new directive with
its own page and examples, and cross-link it from the warning on the
`extractClientIP` page.

Result:
Applications that need the address of the actual peer, for example for
access control or rate limiting, can ask for it directly, and the
distinction is visible where the directives are used rather than only on
the documentation site. `extractClientIP` is unchanged.

Tests:
- sbt "http-tests/testOnly org.apache.pekko.http.scaladsl.server.directives.MiscDirectivesSpec" - pass, 4 new tests including one that asserts the forwarding headers are ignored
- sbt http-tests/test - pass
- sbt docs/test - pass, with new Scala and Java examples
- sbt docs/paradox - pass
- sbt +http/mimaReportBinaryIssues - pass
- sbt http/scalafmt http-tests/Test/scalafmt docs/Test/scalafmt docs/Test/javafmt - clean

References:
None - makes the address that a client cannot spoof directly available
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant