Commit ee4f43f7 by Johannes Edmeier

Ignore security sensitive headers

parent 6eff7914
......@@ -18,9 +18,13 @@ package de.codecentric.boot.admin.server.config;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.HashSet;
import java.util.Set;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.convert.DefaultDurationUnit;
import static java.util.Arrays.asList;
@lombok.Data
@ConfigurationProperties("spring.boot.admin")
public class AdminServerProperties {
......@@ -32,6 +36,8 @@ public class AdminServerProperties {
private MonitorProperties monitor = new MonitorProperties();
private InstanceProxyProperties instanceProxy = new InstanceProxyProperties();
/**
* The metadata keys which should be sanitized when serializing to json
*/
......@@ -77,6 +83,10 @@ public class AdminServerProperties {
this.probedEndpoints = probedEndpoints;
}
public InstanceProxyProperties getInstanceProxy() {
return instanceProxy;
}
@lombok.Data
public static class MonitorProperties {
/**
......@@ -105,4 +115,20 @@ public class AdminServerProperties {
private Duration readTimeout = Duration.ofMillis(5_000);
}
@lombok.Data
public static class InstanceProxyProperties {
/**
* Headers not to be forwarded when making requests to clients.
*/
private Set<String> ignoredHeaders = new HashSet<>(asList("Cookie", "Set-Cookie", "Authorization"));
public Set<String> getIgnoredHeaders() {
return ignoredHeaders;
}
public void setIgnoredHeaders(Set<String> ignoredHeaders) {
this.ignoredHeaders = ignoredHeaders;
}
}
}
......@@ -77,9 +77,11 @@ public class AdminServerWebConfiguration {
public static class ReactiveRestApiConfiguration {
@Bean
@ConditionalOnMissingBean
public InstancesReactiveProxyController instancesProxyController(InstanceRegistry instanceRegistry,
public InstancesReactiveProxyController instancesProxyController(AdminServerProperties adminServerProperties,
InstanceRegistry instanceRegistry,
InstanceWebClient instanceWebClient) {
return new InstancesReactiveProxyController(instanceRegistry, instanceWebClient);
return new InstancesReactiveProxyController(adminServerProperties.getInstanceProxy().getIgnoredHeaders(),
instanceRegistry, instanceWebClient);
}
@Bean
......@@ -102,9 +104,11 @@ public class AdminServerWebConfiguration {
public static class ServletRestApiConfirguation {
@Bean
@ConditionalOnMissingBean
public InstancesServletProxyController instancesProxyController(InstanceRegistry instanceRegistry,
public InstancesServletProxyController instancesProxyController(AdminServerProperties adminServerProperties,
InstanceRegistry instanceRegistry,
InstanceWebClient instanceWebClient) {
return new InstancesServletProxyController(instanceRegistry, instanceWebClient);
return new InstancesServletProxyController(adminServerProperties.getInstanceProxy().getIgnoredHeaders(),
instanceRegistry, instanceWebClient);
}
@Bean
......
......@@ -24,7 +24,10 @@ import reactor.core.publisher.Mono;
import java.net.ConnectException;
import java.net.URI;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -38,16 +41,25 @@ import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ResponseStatusException;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toMap;
public class AbstractInstancesProxyController {
protected static final String REQUEST_MAPPING_PATH = "/instances/{instanceId}/actuator/**";
protected static final String[] HOP_BY_HOP_HEADERS = new String[]{"Connection", "Keep-Alive", "Proxy-Authenticate",
"Proxy-Authorization", "TE", "Trailer", "Transfer-Encoding", "Upgrade", "X-Application-Context"};
protected static final List<String> HOP_BY_HOP_HEADERS = asList("Host", "Connection", "Keep-Alive",
"Proxy-Authenticate", "Proxy-Authorization", "TE", "Trailer", "Transfer-Encoding", "Upgrade",
"X-Application-Context");
private static final Logger log = LoggerFactory.getLogger(AbstractInstancesProxyController.class);
private final InstanceRegistry registry;
private final InstanceWebClient instanceWebClient;
private final Set<String> ignoredHeaders;
public AbstractInstancesProxyController(InstanceRegistry registry, InstanceWebClient instanceWebClient) {
public AbstractInstancesProxyController(Set<String> ignoredHeaders,
InstanceRegistry registry,
InstanceWebClient instanceWebClient) {
this.ignoredHeaders = new HashSet<>(ignoredHeaders);
this.ignoredHeaders.addAll(HOP_BY_HOP_HEADERS);
this.registry = registry;
this.instanceWebClient = instanceWebClient;
}
......@@ -62,11 +74,8 @@ public class AbstractInstancesProxyController {
WebClient.RequestBodySpec bodySpec = instanceWebClient.instance(registry.getInstance(InstanceId.of(instanceId)))
.method(method)
.uri(uri)
.headers(instanceHeaders -> {
instanceHeaders.addAll(headers);
Arrays.stream(HOP_BY_HOP_HEADERS)
.forEach(instanceHeaders::remove);
});
.headers(instanceHeaders -> instanceHeaders.addAll(
filterHeaders(headers)));
WebClient.RequestHeadersSpec<?> headersSpec = bodySpec;
if (requiresBody(method)) {
......@@ -89,6 +98,19 @@ public class AbstractInstancesProxyController {
return new AntPathMatcher().extractPathWithinPattern(REQUEST_MAPPING_PATH, pathWithinApplication);
}
protected HttpHeaders filterHeaders(HttpHeaders headers) {
HttpHeaders filtered = new HttpHeaders();
filtered.putAll(headers.entrySet()
.stream()
.filter(e -> this.includeHeader(e.getKey()))
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue)));
return filtered;
}
private boolean includeHeader(String headers) {
return !ignoredHeaders.contains(headers);
}
private boolean requiresBody(HttpMethod method) {
switch (method) {
case PUT:
......
......@@ -20,7 +20,7 @@ import de.codecentric.boot.admin.server.web.client.InstanceWebClient;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.Arrays;
import java.util.Set;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.bind.annotation.PathVariable;
......@@ -34,8 +34,10 @@ import org.springframework.web.util.UriComponentsBuilder;
*/
@AdminController
public class InstancesReactiveProxyController extends AbstractInstancesProxyController {
public InstancesReactiveProxyController(InstanceRegistry registry, InstanceWebClient instanceWebClient) {
super(registry, instanceWebClient);
public InstancesReactiveProxyController(Set<String> ignoredHeaders,
InstanceRegistry registry,
InstanceWebClient instanceWebClient) {
super(ignoredHeaders, registry, instanceWebClient);
}
@RequestMapping(path = REQUEST_MAPPING_PATH)
......@@ -43,14 +45,15 @@ public class InstancesReactiveProxyController extends AbstractInstancesProxyCont
ServerHttpRequest request,
ServerHttpResponse response) {
String endpointLocalPath = getEndpointLocalPath(request.getPath().pathWithinApplication().value());
URI uri = UriComponentsBuilder.fromPath(endpointLocalPath).query(request.getURI().getRawQuery()).build(true)
URI uri = UriComponentsBuilder.fromPath(endpointLocalPath)
.query(request.getURI().getRawQuery())
.build(true)
.toUri();
return super.forward(instanceId, uri, request.getMethod(), request.getHeaders(),
() -> BodyInserters.fromDataBuffers(request.getBody())).flatMap(clientResponse -> {
response.setStatusCode(clientResponse.statusCode());
response.getHeaders().addAll(clientResponse.headers().asHttpHeaders());
Arrays.stream(HOP_BY_HOP_HEADERS).forEach(response.getHeaders()::remove);
response.getHeaders().addAll(filterHeaders(clientResponse.headers().asHttpHeaders()));
return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers()));
});
}
......
......@@ -21,7 +21,7 @@ import reactor.core.publisher.Mono;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.io.buffer.DataBufferFactory;
......@@ -48,8 +48,10 @@ import org.springframework.web.util.UriComponentsBuilder;
public class InstancesServletProxyController extends AbstractInstancesProxyController {
private final DataBufferFactory bufferFactory = new DefaultDataBufferFactory();
public InstancesServletProxyController(InstanceRegistry registry, InstanceWebClient instanceWebClient) {
super(registry, instanceWebClient);
public InstancesServletProxyController(Set<String> ignoredHeaders,
InstanceRegistry registry,
InstanceWebClient instanceWebClient) {
super(ignoredHeaders, registry, instanceWebClient);
}
@RequestMapping(path = REQUEST_MAPPING_PATH)
......@@ -74,8 +76,7 @@ public class InstancesServletProxyController extends AbstractInstancesProxyContr
DataBufferUtils.readInputStream(request::getBody, this.bufferFactory, 4096)))
.flatMap(clientResponse -> {
response.setStatusCode(clientResponse.statusCode());
response.getHeaders().addAll(clientResponse.headers().asHttpHeaders());
Arrays.stream(HOP_BY_HOP_HEADERS).forEach(response.getHeaders()::remove);
response.getHeaders().addAll(filterHeaders(clientResponse.headers().asHttpHeaders()));
try {
return DataBufferUtils.write(clientResponse.body(BodyExtractors.toDataBuffers()),
response.getBody()).doOnNext(DataBufferUtils::release).then();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment