Skip to content

Commit

Permalink
Return 400 Bad request if request header host is null
Browse files Browse the repository at this point in the history
  • Loading branch information
hamadodene committed Mar 29, 2023
1 parent 69304a3 commit 7eed82c
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ public static final SimpleHTTPResponse MAINTENANCE_MODE(String res) {
return new SimpleHTTPResponse(500, res, null);
}

public static final SimpleHTTPResponse BAD_REQUEST(String res) {
return new SimpleHTTPResponse(400, res, null);
}

@Override
public String toString() {
return "SimpleHTTPResponse{" + "errorcode=" + errorcode + ", resource=" + resource + ", customHeaders=" + customHeaders + '}';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ public Publisher<Void> processRequest(ProxyRequest request) {
return serveInternalErrorMessage(request);
case MAINTENANCE_MODE:
return serverMaintenanceMessage(request);
case BAD_REQUEST:
return serverBadRequestMessage(request);

case STATIC:
case ACME_CHALLENGE:
Expand Down Expand Up @@ -237,6 +239,31 @@ private Publisher<Void> serverMaintenanceMessage(ProxyRequest request) {
return writeSimpleResponse(request, response, customHeaders);
}

private Publisher<Void> serverBadRequestMessage(ProxyRequest request) {
if (request.getResponse().hasSentHeaders()) {
return Mono.empty();
}

SimpleHTTPResponse res = parent.getMapper().mapBadRequest();
int code = 0;
String resource = null;
List<CustomHeader> customHeaders = null;
if (res != null) {
code = res.getErrorcode();
resource = res.getResource();
customHeaders = res.getCustomHeaders();
}
if (resource == null) {
resource = StaticContentsManager.DEFAULT_BAD_REQUEST;
}
if (code <= 0) {
code = 400;
}
FullHttpResponse response = parent.getStaticContentsManager().buildResponse(code, resource);

return writeSimpleResponse(request, response, customHeaders);
}

private Publisher<Void> serveStaticMessage(ProxyRequest request) {
if (request.getResponse().hasSentHeaders()) {
return Mono.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ public class RuntimeServerConfiguration {
private boolean ocspEnabled = false;
private int maxHeaderSize = 8_192; //bytes; default 8kb
private boolean maintenanceModeEnabled = false;
private boolean badRequestOnHttpHeaderHostNull = true;
private boolean http10BackwardCompatibilityEnabled = false;
private String localCertificatesStorePath;
private Set<String> localCertificatesStorePeersIds;
Expand Down Expand Up @@ -243,6 +244,9 @@ public void configure(ConfigurationStore properties) throws ConfigurationNotVali
maintenanceModeEnabled = properties.getBoolean("carapace.maintenancemode.enabled", maintenanceModeEnabled);
LOG.log(Level.INFO, "carapace.maintenancemode.enabled={0}", maintenanceModeEnabled);

badRequestOnHttpHeaderHostNull = properties.getBoolean("carapace.badrequestonhttpheaderhostnull.enabled", badRequestOnHttpHeaderHostNull);
LOG.log(Level.INFO, "carapace.badrequestonhttpheaderhostnull.enabled={0}", badRequestOnHttpHeaderHostNull);

http10BackwardCompatibilityEnabled = properties.getBoolean("carapace.http10backwardcompatibility.enabled", http10BackwardCompatibilityEnabled);
LOG.log(Level.INFO, "carapace.http10backwardcompatibility.enabled={0}", http10BackwardCompatibilityEnabled);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class StaticContentsManager {
public static final String DEFAULT_NOT_FOUND = CLASSPATH_RESOURCE + "/default-error-pages/404_notfound.html";
public static final String DEFAULT_INTERNAL_SERVER_ERROR = CLASSPATH_RESOURCE + "/default-error-pages/500_internalservererror.html";
public static final String DEFAULT_MAINTENANCE_MODE_ERROR = CLASSPATH_RESOURCE + "/default-error-pages/500_maintenance.html";
public static final String DEFAULT_BAD_REQUEST = CLASSPATH_RESOURCE + "/default-error-pages/400_badrequest.html";

private static final Logger LOG = Logger.getLogger(StaticContentsManager.class.getName());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public class RouteConfiguration {
private String errorAction;
private String maintenanceModeAction;

private String badRequestAction;

public RouteConfiguration(String id, String action, boolean enabled, RequestMatcher matcher) {
this.id = id;
this.action = action;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ public SimpleHTTPResponse mapMaintenanceMode(String routeId) {
return SimpleHTTPResponse.MAINTENANCE_MODE(StaticContentsManager.DEFAULT_MAINTENANCE_MODE_ERROR);
}

public SimpleHTTPResponse mapBadRequest() {
return SimpleHTTPResponse.BAD_REQUEST(StaticContentsManager.DEFAULT_BAD_REQUEST);
}

public abstract void configure(ConfigurationStore properties) throws ConfigurationNotValidException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ public static enum Action {
* Maintenance mode
*/
MAINTENANCE_MODE,
/**
* Bad request
*/
BAD_REQUEST,
/**
* Custom static message
*/
Expand Down Expand Up @@ -102,4 +106,11 @@ public static MapResult maintenanceMode(String routeId) {
.build();
}

public static MapResult badRequest() {
return MapResult.builder()
.action(Action.BAD_REQUEST)
.build();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
import static org.carapaceproxy.core.StaticContentsManager.DEFAULT_NOT_FOUND;
import static org.carapaceproxy.core.StaticContentsManager.IN_MEMORY_RESOURCE;
import static org.carapaceproxy.server.config.DirectorConfiguration.ALL_BACKENDS;

import io.netty.handler.codec.http.HttpResponseStatus;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -35,6 +37,7 @@
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.carapaceproxy.SimpleHTTPResponse;
import org.carapaceproxy.configstore.ConfigurationStore;
import org.carapaceproxy.core.ProxyRequest;
Expand Down Expand Up @@ -70,6 +73,7 @@ public class StandardEndpointMapper extends EndpointMapper {
private String forceDirectorParameter = "x-director";
private String forceBackendParameter = "x-backend";
private String defaultMaintenanceAction = "maintenance";
private String defaultBadRequestAction = "bad-request";

private static final Logger LOG = Logger.getLogger(StandardEndpointMapper.class.getName());
private static final String ACME_CHALLENGE_URI_PATTERN = "/\\.well-known/acme-challenge/";
Expand Down Expand Up @@ -113,6 +117,16 @@ public List<String> selectBackends(String userId, String sessionId, String direc

@Override
public MapResult map(ProxyRequest request) {
//If header host is null return bad request
if (request.getRequestHostname() == null &&
parent.getCurrentConfiguration().isBadRequestOnHttpHeaderHostNull()) {

if (LOG.isLoggable(Level.FINER)) {
LOG.log(Level.FINER, "Request " + request.getUri() + " header host is null");
}
return MapResult.badRequest();
}

for (RouteConfiguration route : routes) {
if (!route.isEnabled()) {
continue;
Expand All @@ -122,10 +136,11 @@ public MapResult map(ProxyRequest request) {
if (LOG.isLoggable(Level.FINER)) {
LOG.log(Level.FINER, "route {0}, map {1} -> {2}", new Object[]{route.getId(), request.getUri(), matchResult});
}

if (matchResult) {
if(parent.getCurrentConfiguration().isMaintenanceModeEnabled()) {
if(LOG.isLoggable(Level.FINER)) {
LOG.log(Level.FINER, "Maintenance mode is enable: request uri: {0}",request.getUri());
if (parent.getCurrentConfiguration().isMaintenanceModeEnabled()) {
if (LOG.isLoggable(Level.FINER)) {
LOG.log(Level.FINER, "Maintenance mode is enable: request uri: {0}", request.getUri());
}
return MapResult.maintenanceMode(route.getId());
}
Expand Down Expand Up @@ -254,10 +269,10 @@ public SimpleHTTPResponse mapInternalError(String routeid) {
}

@Override
public SimpleHTTPResponse mapMaintenanceMode(String routeid) {
public SimpleHTTPResponse mapMaintenanceMode(String routeId) {
ActionConfiguration maintenanceAction = null;
// custom for route
Optional<RouteConfiguration> config = routes.stream().filter(r -> r.getId().equalsIgnoreCase(routeid)).findFirst();
Optional<RouteConfiguration> config = routes.stream().filter(r -> r.getId().equalsIgnoreCase(routeId)).findFirst();
if (config.isPresent()) {
String action = config.get().getMaintenanceModeAction();
if (action != null) {
Expand All @@ -272,11 +287,24 @@ public SimpleHTTPResponse mapMaintenanceMode(String routeid) {
return new SimpleHTTPResponse(maintenanceAction.getErrorCode(), maintenanceAction.getFile(), maintenanceAction.getCustomHeaders());
}
// fallback
return super.mapMaintenanceMode(routeid);
return super.mapMaintenanceMode(routeId);
}

@Override
public SimpleHTTPResponse mapPageNotFound(String routeid) {
public SimpleHTTPResponse mapBadRequest() {
// custom global
if (defaultBadRequestAction != null) {
ActionConfiguration errorAction = actions.get(defaultBadRequestAction);
if (errorAction != null) {
return new SimpleHTTPResponse(errorAction.getErrorCode(), errorAction.getFile(), errorAction.getCustomHeaders());
}
}
// fallback
return super.mapBadRequest();
}

@Override
public SimpleHTTPResponse mapPageNotFound(String routeId) {
// custom global
if (defaultNotFoundAction != null) {
ActionConfiguration errorAction = actions.get(defaultNotFoundAction);
Expand All @@ -285,7 +313,7 @@ public SimpleHTTPResponse mapPageNotFound(String routeid) {
}
}
// fallback
return super.mapPageNotFound(routeid);
return super.mapPageNotFound(routeId);
}

@Override
Expand Down Expand Up @@ -313,6 +341,8 @@ public void configure(ConfigurationStore properties) throws ConfigurationNotVali
LOG.log(Level.INFO, "configured default.action.internalerror={0}", defaultInternalErrorAction);
this.defaultMaintenanceAction = properties.getString("default.action.maintenance", "maintenance");
LOG.log(Level.INFO, "configured default.action.maintenance={0}", defaultMaintenanceAction);
this.defaultBadRequestAction = properties.getString("default.action.badrequest", "bad-request");
LOG.log(Level.INFO, "configured default.action.badrequest={0}", defaultBadRequestAction);
this.forceDirectorParameter = properties.getString("mapper.forcedirector.parameter", forceDirectorParameter);
LOG.log(Level.INFO, "configured mapper.forcedirector.parameter={0}", forceDirectorParameter);
this.forceBackendParameter = properties.getString("mapper.forcebackend.parameter", forceBackendParameter);
Expand Down Expand Up @@ -466,9 +496,9 @@ public void configure(ConfigurationStore properties) throws ConfigurationNotVali
config.setErrorAction(errorAction);
//Maintenance action
String maintenanceAction = properties.getString(prefix + "maintenanceaction", "");
if(!maintenanceAction.isEmpty()) {
if (!maintenanceAction.isEmpty()) {
ActionConfiguration defined = actions.get(maintenanceAction);
if(defined == null || !ActionConfiguration.TYPE_STATIC.equals(defined.getType())) {
if (defined == null || !ActionConfiguration.TYPE_STATIC.equals(defined.getType())) {
throw new ConfigurationNotValidException("Maintenance action for route " + id + " has to be defined and has to be type STATIC");
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<html>
<body>
Bad request
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,7 @@ public void NullHostHeaderTest() throws Exception {

//No Http header host
String response = sendRequest(false, hostname, port, path);
// no response because NullPointerException occurred before.
// java.lang.NullPointerException: Cannot invoke "java.lang.CharSequence.length()" because "this.text" is null
assertFalse(response.contains("it <b>works</b> !!"));
assertTrue(response.contains("Bad request"));

//Add Http header host
String response2 = sendRequest(true, hostname, port, path);
Expand Down

0 comments on commit 7eed82c

Please sign in to comment.