-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #154 from groldan/ows_through_proxy
Cascaded OWS Stores with HTTP proxy externalized configuration
- Loading branch information
Showing
10 changed files
with
725 additions
and
1 deletion.
There are no files selected for viewing
Submodule config
updated
9 files
+27 −0 | application.yml | |
+1 −0 | catalog-service.yml | |
+0 −1 | gateway-service.yml | |
+1 −0 | restconfig-v1.yml | |
+1 −1 | wcs-service.yml | |
+1 −0 | web-ui.yml | |
+1 −1 | wfs-service.yml | |
+1 −0 | wms-service.yml | |
+1 −0 | wps-service.yml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# Cloud Native GeoServer externalized configuration guide | ||
|
||
## Contents | ||
{:.no_toc} | ||
|
||
* Will be replaced with the ToC, excluding the "Contents" header | ||
{:toc} | ||
|
||
## Introduction | ||
|
||
TBD | ||
|
||
## GeoServer configuration properties | ||
|
||
## HTTP proxy for cascaded OWS (WMS/WMTS/WFS) Stores | ||
|
||
Cascaded OWS stores make use of a SPI (Service Provider Interface) | ||
extension point to configure the appropriate GeoTools `HTTPClientFactory`. | ||
|
||
We provide a Spring Boot Auto-configuration that can be configured | ||
through regular spring-boot externalized properties and only affects | ||
GeoTools HTTP clients instead of the whole JVM. | ||
|
||
The usual way to set an http proxy is through the `http.proxyHost`, `http.proxyPort`, | ||
`http.proxyUser`, `http.proxyPassword` Java System Properties. | ||
|
||
In the context of Cloud Native GeoServer containerized applications, | ||
this presents a number of drawbacks: | ||
|
||
* Standard Java proxy parameters only work with System properties, | ||
not OS environment variables, and setting system properties is more | ||
cumbersome than env variables (you have to modify the container run command). | ||
* `http.proxyUser/Password` are not standard properties, though commonly used, it's kind of | ||
JDK implementation dependent. | ||
* Setting `-Dhttp.proxy* System properties affects all HTTP clients in the container, meaning | ||
requests to the config-service, discovery-service, etc., will also try to go through the proxy, | ||
or you need to go through the extra burden of figuring out how to ignore them. | ||
* If the proxy is secured, and since the http client used may not respect the | ||
http.proxyUser/Password parameters, the apps won't start since they'll get | ||
HTTP 407 "Proxy Authentication Required". | ||
|
||
The following externalized configuration properties apply, with these suggested default values: | ||
|
||
```yaml | ||
# GeoTools HTTP Client proxy configuration, allows configuring cascaded WMS/WMTS/WFS stores | ||
# that need to go through an HTTP proxy without affecting all the http clients at the JVM level | ||
# These are default settings. The enabled property can be set to false to disable the custom | ||
# HTTPClientFactory altogether. | ||
# The following OS environment variables can be set for easier configuration: | ||
# HTTP(S)_PROXYHOST, HTTP(S)_PROXYPORT, HTTP(S)_PROXYUSER, HTTP(S)_PROXYPASSWORD, HTTP(S)_NONPROXYHOSTS | ||
geotools: | ||
httpclient: | ||
proxy: | ||
enabled: true | ||
http: | ||
host: ${http.proxyHost:} | ||
port: ${http.proxyPort:} | ||
user: ${http.proxyUser:} | ||
password: ${http.proxyPassword:} | ||
nonProxyHosts: ${http.nonProxyHosts:localhost.*} | ||
# comma separated list of Java regular expressions, e.g.: nonProxyHosts: localhost, example.* | ||
https: | ||
host: ${https.proxyHost:${geotools.httpclient.proxy.http.host}} | ||
port: ${https.proxyPort:${geotools.httpclient.proxy.http.port}} | ||
user: ${https.proxyUser:${geotools.httpclient.proxy.http.user}} | ||
password: ${https.proxyPassword:${geotools.httpclient.proxy.http.password}} | ||
nonProxyHosts: ${https.nonProxyHosts:${geotools.httpclient.proxy.http.nonProxyHosts}} | ||
``` | ||
### Configure HTTP proxy with environment variables in docker-compose.yml | ||
As mentioned above, regular JVM proxy configuration works with Java System properties | ||
but not with Operating System environment variables. | ||
The above `geotools.httpclient.proxy` config properties though allow to do so | ||
easily as in the following `docker-compose.yml` snippet: | ||
|
||
```yaml | ||
version: "3.8" | ||
... | ||
services: | ||
... | ||
wms: | ||
image: geoservercloud/geoserver-cloud-wms:<version> | ||
environment: | ||
HTTP_PROXYHOST: 192.168.86.26 | ||
HTTP_PROXYPORT: 80 | ||
HTTP_PROXYUSER: jack | ||
HTTP_PROXYPASSWORD: insecure | ||
... | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
.../main/java/org/geotools/autoconfigure/httpclient/GeoToolsHttpClientAutoConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/* | ||
* (c) 2021 Open Source Geospatial Foundation - all rights reserved This code is licensed under the | ||
* GPL 2.0 license, available at the root application directory. | ||
*/ | ||
package org.geotools.autoconfigure.httpclient; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.geotools.autoconfigure.httpclient.ProxyConfig.ProxyHostConfig; | ||
import org.geotools.http.HTTPClientFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
import org.springframework.boot.context.properties.ConfigurationProperties; | ||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
/** | ||
* {@link EnableAutoConfiguration @EnableAutoConfiguration} auto configuration for a GeoTools {@link | ||
* HTTPClientFactory} that can be configured through spring-boot externalized properties and only | ||
* affects GeoTools http clients instead of the whole JVM. | ||
* | ||
* <p>The usual way to set an http proxy is through the {@literal http.proxyHost}, {@literal | ||
* http.proxyPort}, {@literal http.proxyUser}, {@literal http.proxyPassword} Java System Properties. | ||
* | ||
* <p>In the context of Cloud Native GeoServer containerized applications, this has a number of | ||
* drawbacks: | ||
* | ||
* <ul> | ||
* <li>Standard java proxy parameters only work with System properties, not env variables (at | ||
* least with the apache http client), and setting system properties is more cumbersome than | ||
* env variables (you have to modify the container run command) | ||
* <li>{@literal http.proxyUser/Password} are not standard properties, though commonly used, it's | ||
* kind of JDK implementation dependent. | ||
* <li>Setting {@literal -Dhtt.proxy*} System properties affects all HTTP clients in the | ||
* container, meaning requests to the {@literal config-service}, {@literal discovery-service}, | ||
* etc., will also try to go through the proxy, or you need to go through the extra burden of | ||
* figuring out how to ignore them. | ||
* <li>If the proxy is secured, and since the http client used may not respect the {@literal | ||
* http.proxyUser/Password} parameters, the apps won't start since they'll get HTTP 407 "Proxy | ||
* Authentication Required". | ||
* </ul> | ||
* | ||
* <p>The following externalized configuration properties apply: | ||
* | ||
* <pre> | ||
* <code> | ||
* geotools: | ||
* httpclient: | ||
* proxy: | ||
* # defaults to true, false disables the autoconfiguration and falls back to standard GeoServer behavior | ||
* enabled: true | ||
* http: | ||
* host: | ||
* port: | ||
* user: | ||
* password: | ||
* nonProxyHosts: | ||
* # comma separated list of Java regular expressions, e.g.: nonProxyHosts: localhost, example.* | ||
* https: | ||
* host: | ||
* port: | ||
* user: | ||
* password: | ||
* nonProxyHosts: | ||
* </code> | ||
* </pre> | ||
*/ | ||
@Configuration(proxyBeanMethods = false) | ||
@EnableConfigurationProperties | ||
@ConditionalOnProperty( | ||
prefix = "geotools.httpclient.proxy", | ||
name = "enabled", | ||
havingValue = "true", | ||
matchIfMissing = true | ||
) | ||
@Slf4j(topic = "org.geotools.autoconfigure.httpclient") | ||
public class GeoToolsHttpClientAutoConfiguration { | ||
|
||
@ConfigurationProperties(prefix = "geotools.httpclient.proxy") | ||
public @Bean ProxyConfig geoToolsHttpProxyConfiguration() { | ||
System.setProperty( | ||
"HTTP_CLIENT_FACTORY", | ||
SpringEnvironmentAwareGeoToolsHttpClientFactory.class.getCanonicalName()); | ||
return new ProxyConfig(); | ||
} | ||
|
||
public @Bean SpringEnvironmentAwareGeoToolsHttpClientFactory | ||
springEnvironmentAwareGeoToolsHttpClientFactory(@Autowired ProxyConfig proxyConfig) { | ||
|
||
log.info("Using spring environment aware GeoTools HTTPClientFactory"); | ||
log(proxyConfig.getHttp(), "HTTP"); | ||
log(proxyConfig.getHttps(), "HTTPS"); | ||
SpringEnvironmentAwareGeoToolsHttpClientFactory.setProxyConfig(proxyConfig); | ||
|
||
return new SpringEnvironmentAwareGeoToolsHttpClientFactory(); | ||
} | ||
|
||
private void log(ProxyHostConfig config, String protocol) { | ||
config.host() | ||
.ifPresentOrElse( | ||
host -> | ||
log.info( | ||
"{} proxy configured for GeoTools cascaded OWS stores: {}:{}, secured: {}", | ||
protocol, | ||
host, | ||
config.port(), | ||
config.isSecured()), | ||
() -> | ||
log.info( | ||
"No {} proxy configured for GeoTools cascaded OWS stores", | ||
protocol)); | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
...alog-backend-starter/src/main/java/org/geotools/autoconfigure/httpclient/ProxyConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/* | ||
* (c) 2021 Open Source Geospatial Foundation - all rights reserved This code is licensed under the | ||
* GPL 2.0 license, available at the root application directory. | ||
*/ | ||
package org.geotools.autoconfigure.httpclient; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.regex.Pattern; | ||
import lombok.Data; | ||
import lombok.NonNull; | ||
import org.springframework.util.StringUtils; | ||
|
||
/** */ | ||
public @Data class ProxyConfig { | ||
|
||
private boolean enabled = true; | ||
private ProxyHostConfig http = new ProxyHostConfig(); | ||
private ProxyHostConfig https = new ProxyHostConfig(); | ||
|
||
public static @Data class ProxyHostConfig { | ||
private String host; | ||
private Integer port; | ||
private String user; | ||
private String password; | ||
private List<Pattern> nonProxyHosts; | ||
|
||
public Optional<ProxyHostConfig> forHost(@NonNull String targetHostname) { | ||
if (host().isEmpty()) { | ||
return Optional.empty(); | ||
} | ||
for (Pattern p : nonProxyHosts()) { | ||
if (p.matcher(targetHostname).matches()) { | ||
return Optional.empty(); | ||
} | ||
} | ||
return Optional.of(this); | ||
} | ||
|
||
public List<Pattern> nonProxyHosts() { | ||
return this.nonProxyHosts == null ? List.of() : this.nonProxyHosts; | ||
} | ||
|
||
public Optional<String> host() { | ||
return StringUtils.hasLength(this.host) ? Optional.of(this.host) : Optional.empty(); | ||
} | ||
|
||
public int port() { | ||
return port == null ? 80 : port.intValue(); | ||
} | ||
|
||
public boolean isSecured() { | ||
return StringUtils.hasLength(host) | ||
&& StringUtils.hasLength(user) | ||
&& StringUtils.hasLength(password); | ||
} | ||
} | ||
|
||
public ProxyHostConfig ofProtocol(@NonNull String protocol) { | ||
if ("http".equals(protocol)) return http == null ? new ProxyHostConfig() : http; | ||
if ("https".equals(protocol)) return https == null ? new ProxyHostConfig() : https; | ||
throw new IllegalArgumentException("Uknown protocol " + protocol + ". Expected http(s)"); | ||
} | ||
} |
Oops, something went wrong.