diff --git a/appserver/admingui/common/src/main/java/org/glassfish/admingui/common/security/AdminConsoleAuthModule.java b/appserver/admingui/common/src/main/java/org/glassfish/admingui/common/security/AdminConsoleAuthModule.java index 98bd3075748..4d43fd92499 100644 --- a/appserver/admingui/common/src/main/java/org/glassfish/admingui/common/security/AdminConsoleAuthModule.java +++ b/appserver/admingui/common/src/main/java/org/glassfish/admingui/common/security/AdminConsoleAuthModule.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation. All rights reserved. + * Copyright (c) 2022, 2026 Contributors to the Eclipse Foundation. All rights reserved. * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -17,9 +17,11 @@ package org.glassfish.admingui.common.security; +import com.sun.enterprise.config.serverbeans.Config; import com.sun.enterprise.config.serverbeans.Domain; import com.sun.enterprise.config.serverbeans.SecureAdmin; import com.sun.enterprise.security.SecurityServicesUtil; +import com.sun.enterprise.util.net.NetUtils; import jakarta.security.auth.message.AuthException; import jakarta.security.auth.message.AuthStatus; @@ -56,6 +58,7 @@ import org.glassfish.admingui.common.util.RestUtil; import org.glassfish.common.util.InputValidationUtil; import org.glassfish.grizzly.config.dom.NetworkListener; +import org.glassfish.grizzly.config.dom.Protocol; import org.glassfish.hk2.api.ServiceLocator; import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature; @@ -120,10 +123,8 @@ public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy throw new AuthException( "'loginErrorPage' " + "must be supplied as a property in the provider-config " + "in the domain.xml file!"); } - ServiceLocator habitat = SecurityServicesUtil.getInstance().getHabitat(); - Domain domain = habitat.getService(Domain.class); - NetworkListener adminListener = domain.getServerNamed("server").getConfig().getNetworkConfig() - .getNetworkListener("admin-listener"); + ServiceLocator habitat = getServiceLocator(); + NetworkListener adminListener = getAdminListener(); SecureAdmin secureAdmin = habitat.getService(SecureAdmin.class); final String host = adminListener.getAddress(); @@ -133,6 +134,16 @@ public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy } } + private static ServiceLocator getServiceLocator() { + return SecurityServicesUtil.getInstance().getHabitat(); + } + + private static NetworkListener getAdminListener() { + Config config = getServiceLocator().getService(Domain.class) + .getServerNamed("server").getConfig(); + return config.getAdminListener(); + } + /** * */ @@ -206,10 +217,15 @@ public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject Client client2 = RestUtil.initialize(ClientBuilder.newBuilder()).build(); WebTarget target = client2.target(restURL); target.register(HttpAuthenticationFeature.basic(username, new String(password))); + + // Get the real remote host, checking for proxy headers if behind a reverse proxy + String remoteHost = getRemoteHost(request); MultivaluedMap payLoad = new MultivaluedHashMap(); - payLoad.putSingle("remoteHostName", request.getRemoteHost()); + payLoad.putSingle("remoteHostName", remoteHost); - Response resp = target.request(RESPONSE_TYPE).post(Entity.entity(payLoad, MediaType.APPLICATION_FORM_URLENCODED), Response.class); + Response resp = target.request(RESPONSE_TYPE) + .header("X-GlassFish-Remote-Host", remoteHost) + .post(Entity.entity(payLoad, MediaType.APPLICATION_FORM_URLENCODED), Response.class); RestResponse restResp = RestResponse.getRestResponse(resp); Arrays.fill(password, ' '); // Check to see if successful.. @@ -270,7 +286,11 @@ public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject return AuthStatus.SEND_CONTINUE; } else { int status = restResp.getResponseCode(); - if (status == 403) { + if (status == 429) { + // Too many concurrent requests - rate limited + request.setAttribute("errorText", GuiUtil.getMessage("alert.AuthenticationFailed")); + request.setAttribute("messageText", GuiUtil.getMessage("alert.TryAgainLater")); + } else if (status == 403) { request.setAttribute("errorText", GuiUtil.getMessage("alert.ConfigurationError")); request.setAttribute("messageText", GuiUtil.getMessage("alert.EnableSecureAdmin")); } @@ -301,4 +321,31 @@ public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthEx private boolean isMandatory(MessageInfo messageInfo) { return Boolean.valueOf((String) messageInfo.getMap().get("jakarta.security.auth.message.MessagePolicy.isMandatory")); } + + /** + * Gets the real remote host, checking for proxy headers if behind a reverse proxy. + * Only uses proxy headers when behindProxy is enabled in configuration. + */ + private String getRemoteHost(HttpServletRequest request) { + // Check if behind proxy is enabled + Protocol protocol = getAdminListener().findHttpProtocol(); + boolean behindProxy = protocol != null && protocol.getHttp() != null + && protocol.getHttp().isBehindProxy(); + + return NetUtils.getRemoteHost( + new NetUtils.RequestInfoProvider() { + @Override + public String getHeader(String name) { + return request.getHeader(name); + } + + @Override + public String getRemoteHost() { + return request.getRemoteHost(); + } + + }, + behindProxy + ); + } } diff --git a/appserver/admingui/core/src/main/resources/org/glassfish/admingui/core/Strings.properties b/appserver/admingui/core/src/main/resources/org/glassfish/admingui/core/Strings.properties index 8db4a34c015..81532381ec0 100644 --- a/appserver/admingui/core/src/main/resources/org/glassfish/admingui/core/Strings.properties +++ b/appserver/admingui/core/src/main/resources/org/glassfish/admingui/core/Strings.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2023 Contributors to the Eclipse Foundation. +# Copyright (c) 2023, 2026 Contributors to the Eclipse Foundation. # Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. # # This program and the accompanying materials are made available under the @@ -1016,6 +1016,7 @@ alert.AuthenticationFailed=Authentication Failed alert.ReenterUsernamePassword=Re-enter your username and password alert.ConfigurationError=Configuration Error alert.EnableSecureAdmin=Secure Admin must be enabled to access the DAS remotely. +alert.TryAgainLater=Authentication refused, try again later. ## Recover Transactions headings.recoverTransactions=Recover Transactions diff --git a/appserver/admingui/web/src/main/resources/grizzly/http.layout b/appserver/admingui/web/src/main/resources/grizzly/http.layout index ddcd9ce8d6c..b3eb7ba8c13 100644 --- a/appserver/admingui/web/src/main/resources/grizzly/http.layout +++ b/appserver/admingui/web/src/main/resources/grizzly/http.layout @@ -1,5 +1,6 @@