Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require SSL mode "External requests" does not work with IPv6 local addresses #30678

Open
1 of 2 tasks
tsaarni opened this issue Jun 24, 2024 · 3 comments · May be fixed by #30751
Open
1 of 2 tasks

Require SSL mode "External requests" does not work with IPv6 local addresses #30678

tsaarni opened this issue Jun 24, 2024 · 3 comments · May be fixed by #30751
Labels

Comments

@tsaarni
Copy link
Contributor

tsaarni commented Jun 24, 2024

Before reporting an issue

  • I have read and understood the above terms for submitting issues, and I understand that my issue may be closed without action if I do not follow them.

Area

core

Describe the bug

When using IPv6 and client is sending request from link-local or unique local address (ULA), client is blocked from sending clear-text HTTP requests when realm configuration is set to require SSL for "External request" (link).

Version

main

Regression

  • The issue is a regression

Expected behavior

Requests sent from IPv6 link-local or unique local address should succeed.

Actual behavior

Requests are rejected with response 403 Forbidden with body

{
    "error": "invalid_request",
    "error_description": "HTTPS required"
}

How to Reproduce?

One way to reproduce is to use Kubernetes cluster with IPv6 enabled

  1. Run Keycloak in a Kubernetes cluster with IPv6 enabled.
  2. Run a client in another pod and check that the pod's network interface has IPv6 address either from link-local address range or unique local address range. Send request using IPv6.
  3. Observe response 403 Forbidden

For example for local development, Kind would use link-local addresses for pods and services within the cluster and a "real" CNI like Calico would typically use unique local addresses (ULA).

Anything else?

The code that checks for the request is here

InetAddress inetAddress = InetAddress.getByName(remoteAddress);
return inetAddress.isAnyLocalAddress() || inetAddress.isLoopbackAddress() || inetAddress.isSiteLocalAddress();

Looking at the git history, it seems that initially the code aimed to cover only clients within localhost, and later it took the current form where it was extended to allow IPv4 private addresses (link).

In case of IPv6 the private address ranges are following:

address type binary prefix IPv6 notation
Link-local unicast 1111 1110 10 FE80::/10
Unique local (ULA) 1111 110 FC00::/7
Site-local unicast (deprecated) 1111 1110 11 FEF0::/10

While the JDK method Inet4Address.isSiteLocalAddress() covers private address ranges for IPv4 (link), the IPv6 version Inet6Address.isSiteLocalAddress() only covers site-local unicast address range (link). According to RFC4291 section2.5.7 this address range is now deprecated and should be considered as global address by new implementation

The special behavior of this prefix defined in [RFC3513] must no longer be supported in new implementations (i.e., new implementations must treat this prefix as Global Unicast).

Existing implementations and deployments may continue to use this prefix.

While the documentation for interface InetAddress.isSiteLocalAddress() says (link)

Utility routine to check if the InetAddress is a site local address.

it seems likely to me that applications may rather use it for the effect of "all private address ranges", than for the specific (now deprecated) IPv6 site-local definition, especially considering the method is used through the upper class that covers both IPv4 and IPv6. Still, the JDK IPv6 implementation seems to take strict interpretation of covering site-local only and does not offer any method for checking for IPv6 private address ranges.

@tsaarni tsaarni added kind/bug Categorizes a PR related to a bug status/triage labels Jun 24, 2024
@tsaarni tsaarni linked a pull request Jun 25, 2024 that will close this issue
@ahus1
Copy link
Contributor

ahus1 commented Jun 28, 2024

Hi @tsaarni - thank you for raising this issue.

IMHO the concept of some request requiring TLS, while others don't, might something that worked in the past where we divided the world into external, untrusted networks and internal, trusted networks. This is IMHO no longer the case in a world where zero trust is promoted.

So I'd say this option didn't age well, and it should rather be removed from Keycloak, than being extended. Using plain HTTP in a development environment might work, still in a production environment all requests should use HTTPS.

I'd be happy to hear your thoughts on this, especially when this concept should still be used.

cc: @stianst

@tsaarni
Copy link
Contributor Author

tsaarni commented Jun 28, 2024

We sometimes use it inside Kubernetes cluster. TLS is terminated at the ingress controller, at the edge of the cluster internal network. The internal "connection leg" can be optionally configured for clear-text HTTP like this:

image

@ahus1
Copy link
Contributor

ahus1 commented Jun 28, 2024

Thank you for providing the diagram. I assume the ingress controller adds HTTP headers which Keycloak then uses to identify the original IP address used by the external client to connect to the ingress controller, and it will also use those extra HTTP headers to identify that the request originated via HTTPS?

If we would drop the check altogether, is there any use case that would not work any more for you?

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 a pull request may close this issue.

2 participants