apache/shenyu · error · IllegalArgumentException
Access to localhost is not allowed
Error message
Access to localhost is not allowed
What it means
As part of SSRF defense, validateHostForSSRF rejects hostnames that resolve to localhost (localhost, 127.x, ::1, etc.) and throws IllegalArgumentException "Access to localhost is not allowed". This prevents the admin server from being tricked into calling its own loopback interface where internal admin APIs would be reachable.
Solutions
- Use the backend service's real network address (container/service DNS name, LAN IP, or public domain) instead of localhost.
- In Docker Compose/K8s, use service names (e.g. http://backend:8080) rather than localhost or 127.0.0.1.
- Do not disable or bypass this check in production; it is an intentional SSRF safeguard.
- If the private-network policy is too strict for your topology, adjust allowlists consciously with a security review rather than catching and ignoring the exception.
Example fix
// before
validateUrlForSSRF("http://localhost:8080/actuator/health");
// after
validateUrlForSSRF("http://backend-service:8080/actuator/health"); Defensive patterns
Strategy: try-catch
Validate before calling
HttpUrl parsed = HttpUrl.parse(url);
String host = parsed != null ? parsed.host().toLowerCase() : "";
boolean isLoopback = "localhost".equals(host) || host.equals("127.0.0.1")
|| host.startsWith("127.") || host.equals("::1") || host.equals("0.0.0.0");
if (isLoopback) throw new IllegalArgumentException("localhost targets are not allowed"); Try / catch
try {
UrlSecurityUtils.validateUrlForSSRF(url);
} catch (IllegalArgumentException e) {
log.warn("Blocked localhost/internal target (SSRF guard): {}", url);
return ResponseEntity.badRequest().body("localhost targets are not allowed");
} Prevention
- Use container/service DNS names (http://backend:8080) instead of localhost in Docker/K8s.
- Record rejected loopback URLs — repeated attempts may indicate probing.
- Educate users that 'localhost' from the admin server means the admin container itself.
- Keep this SSRF guard enabled in all environments; it protects internal admin APIs.
When it happens
Trigger: Configuring a target URL whose host is 'localhost', '127.0.0.1', a loopback alias, or '0.0.0.0' — either by accident (admin meant a local dev backend) or as an SSRF attack payload.
Common situations: Developers testing in-container setups pointing the URL at localhost instead of the service's Docker DNS name; attackers submitting webhook/health-check URLs targeting the gateway's own loopback to hit internal admin endpoints.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- URL cannot be empty
- Invalid URL format
- Only HTTP and HTTPS protocols are allowed
- Host cannot be empty
- Access to private or internal IP addresses is not allowed
AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12).
Data as JSON: /api/errors/253a11b9e0792b71.
Report an issue: GitHub.
Appendix: source
Thrown at shenyu-admin/src/main/java/org/apache/shenyu/admin/utils/UrlSecurityUtils.java:87
}
/**
* Validate host to prevent SSRF attacks.
*
* @param host the host to validate
* @param port the port to validate
* @throws IllegalArgumentException if the host is not allowed
*/
public static void validateHostForSSRF(final String host, final int port) {
if (Objects.isNull(host) || host.trim().isEmpty()) {
throw new IllegalArgumentException("Host cannot be empty");
}
String normalizedHost = host.toLowerCase().trim();
// Check for localhost variations
if (isLocalhost(normalizedHost)) {
throw new IllegalArgumentException("Access to localhost is not allowed");
}
// Check for private IP addresses
if (isPrivateOrInternalIP(normalizedHost)) {
throw new IllegalArgumentException("Access to private or internal IP addresses is not allowed");
}
// Check for sensitive ports
if (isSensitivePort(port)) {
throw new IllegalArgumentException("Access to sensitive ports is not allowed");
}
// Additional validation for DNS resolution
try {
InetAddress[] addresses = InetAddress.getAllByName(normalizedHost);
for (InetAddress address : addresses) {
if (address.isLoopbackAddress() || address.isLinkLocalAddress()
|| address.isSiteLocalAddress() || address.isAnyLocalAddress()) {View on GitHub (pinned to 567142e072)