javaspringspring-bootazurespring-mvc

Spring Boot deployement fails to parse IP address


I have a Spring Boot deployment spun up on Azure using Spring Boot 3 (specifically 3.3.1). Because of some reasons outside of my control, some specific IP addresses need to be permitted to access a specific set of pages.

I currently have a portion of my security config set up as follows:

.requestMatchers("/example1", "/example2")
.access(new WebExpressionAuthorizationManager(
    "isAuthenticated() or (isAnonymous() and (hasIpAddress(123.456.0.1) or hasIpAddress(654.321.0.1))"
))

When running locally (localhost), I can connect to the allowed pages without authentication just fine, as long as I am connecting from the supplied address. However, once I move to my Azure instance, everytime I try to connect to one of the sites specified in the requestMatcher (example1, etc) I get the following error (IP address redacted for security reasons):

2025-02-24T15:41:43.308208721Z: [INFO]  java.lang.IllegalArgumentException: Failed to parse address '123.456.0.1:12345'
2025-02-24T15:41:43.308212747Z: [INFO]      at org.springframework.security.web.util.matcher.IpAddressMatcher.parseAddress(IpAddressMatcher.java:113)
2025-02-24T15:41:43.308215725Z: [INFO]      at org.springframework.security.web.util.matcher.IpAddressMatcher.matches(IpAddressMatcher.java:73)
2025-02-24T15:41:43.308218491Z: [INFO]      at org.springframework.security.web.util.matcher.IpAddressMatcher.matches(IpAddressMatcher.java:68)
2025-02-24T15:41:43.308221057Z: [INFO]      at org.springframework.security.web.access.expression.WebSecurityExpressionRoot.hasIpAddress(WebSecurityExpressionRoot.java:65)
2025-02-24T15:41:43.308441662Z: [INFO]      at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
2025-02-24T15:41:43.308446958Z: [INFO]      at java.base/java.lang.reflect.Method.invoke(Method.java:580)
2025-02-24T15:41:43.308449788Z: [INFO]      at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:142)
2025-02-24T15:41:43.308452517Z: [INFO]      at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:125)
2025-02-24T15:41:43.308455151Z: [INFO]      at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:108)
2025-02-24T15:41:43.308457758Z: [INFO]      at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:222)
2025-02-24T15:41:43.308460311Z: [INFO]      at org.springframework.expression.spel.ast.OpAnd.getBooleanValue(OpAnd.java:57)
2025-02-24T15:41:43.308462918Z: [INFO]      at org.springframework.expression.spel.ast.OpAnd.getValueInternal(OpAnd.java:52)
2025-02-24T15:41:43.308465398Z: [INFO]      at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:222)
2025-02-24T15:41:43.308467918Z: [INFO]      at org.springframework.expression.spel.ast.OpOr.getBooleanValue(OpOr.java:56)
2025-02-24T15:41:43.308470563Z: [INFO]      at org.springframework.expression.spel.ast.OpOr.getValueInternal(OpOr.java:51)
2025-02-24T15:41:43.308473233Z: [INFO]      at org.springframework.expression.spel.ast.OpOr.getValueInternal(OpOr.java:37)
2025-02-24T15:41:43.308475769Z: [INFO]      at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:119)
2025-02-24T15:41:43.308478371Z: [INFO]      at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:309)
2025-02-24T15:41:43.308482590Z: [INFO]      at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:30)
2025-02-24T15:41:43.308485469Z: [INFO]      at org.springframework.security.web.access.expression.WebExpressionAuthorizationManager.check(WebExpressionAuthorizationManager.java:76)
2025-02-24T15:41:43.308488106Z: [INFO]      at org.springframework.security.web.access.expression.WebExpressionAuthorizationManager.check(WebExpressionAuthorizationManager.java:39)
[Lots of trace...]
2025-02-24T15:41:44.764129250Z: [INFO]      at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:389)
2025-02-24T15:41:44.764131941Z: [INFO]      at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
2025-02-24T15:41:44.764134397Z: [INFO]      at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:904)
2025-02-24T15:41:44.764136904Z: [INFO]      at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741)
2025-02-24T15:41:44.764139363Z: [INFO]      at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
2025-02-24T15:41:44.764141809Z: [INFO]      at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)
2025-02-24T15:41:44.764144270Z: [INFO]      at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
2025-02-24T15:41:44.764146752Z: [INFO]      at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
2025-02-24T15:41:44.764149184Z: [INFO]      at java.base/java.lang.Thread.run(Thread.java:1583)
2025-02-24T15:41:44.764154252Z: [INFO]  Caused by: java.net.UnknownHostException: 123.456.0.1:12345: invalid IPv6 address literal
2025-02-24T15:41:44.764156916Z: [INFO]      at java.base/java.net.InetAddress.invalidIPv6LiteralException(InetAddress.java:1693)
2025-02-24T15:41:44.764159465Z: [INFO]      at java.base/java.net.InetAddress.getAllByName(InetAddress.java:1663)
2025-02-24T15:41:44.764161891Z: [INFO]      at java.base/java.net.InetAddress.getByName(InetAddress.java:1568)
2025-02-24T15:41:44.764164362Z: [INFO]      at org.springframework.security.web.util.matcher.IpAddressMatcher.parseAddress(IpAddressMatcher.java:110)
2025-02-24T15:41:44.764166884Z: [INFO]      ... 119 common frames omitted

When I follow the error log, it looks like the issue has to do with the fact that in the security chain, ports cannot be specified, and yet the port is being passed into the security check while in Azure.

How do I prevent the port from being passed as well? Or is there another way around this entirely?


Solution

  • I switched to a different system. Many of the other answers/comments didn't understand that my issue was that Azure was forcing the IP address that was passed into Spring Boot to have a port number.

    I had no way around this, or at least, nobody was able to explain to me why Azure forces a port number on the IP address.

    I resolved the issue by using a custom AuthorizationManager that manually parses out port numbers (if there is exactly one instance of a colon):

    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context)
    {
        Authentication auth = authentication.get();
        
        boolean isAnonymous = auth instanceof AnonymousAuthenticationToken;
        
        String ipAddress = context.getRequest().getRemoteAddr();
        
        if(StringUtils.countOccurrencesOf(ipAddress, ":") > 1) return new AuthorizationDecision(Main.getFloreIPs().contains(ipAddress) || (auth.isAuthenticated() && !isAnonymous));
        
        String trimmedIpAddress = ipAddress.indexOf(":") != -1 ? ipAddress.substring(0, ipAddress.indexOf(":")) : ipAddress;
        return new AuthorizationDecision(Main.getAllowedIps().contains(trimmedIpAddress) || (auth.isAuthenticated() && !isAnonymous));
    }