Skip to content

Commit 1f1d4f2

Browse files
predic8rrayst
andauthored
sslProxy, TLS Tutorials (#2703)
* feat(dispatcher): add production mode support for error handling and extend URI validation tests - Introduced `productionRouter` in `DummyTestRouter` to enable production mode configuration. - Updated `DispatchingInterceptor` to adjust error messages in production mode. - Expanded `DispatchingInterceptorTest` with production-specific URI validation. * refactor(core): extract `Target` class for reuse and update references in proxies and tests * refactor(core): consolidate imports in `SSLProxy` and apply formatting updates * correctly open SSL-based port, even if SSLproxy has no SSLableProxy on the same port; warn of default certificate also when loading it from a PEM file --------- Co-authored-by: Tobias Polley <mail@tobias-polley.de>
1 parent f345a9b commit 1f1d4f2

17 files changed

Lines changed: 420 additions & 252 deletions

File tree

core/src/main/java/com/predic8/membrane/core/proxies/AbstractServiceProxy.java

Lines changed: 0 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,8 @@
1717
import com.predic8.membrane.annot.*;
1818
import com.predic8.membrane.core.config.*;
1919
import com.predic8.membrane.core.config.security.*;
20-
import com.predic8.membrane.core.config.xml.*;
21-
import com.predic8.membrane.core.exchange.*;
22-
import com.predic8.membrane.core.interceptor.*;
23-
import com.predic8.membrane.core.lang.*;
24-
import com.predic8.membrane.core.lang.ExchangeExpression.*;
25-
import com.predic8.membrane.core.router.*;
2620
import com.predic8.membrane.core.transport.ssl.*;
2721

28-
import static com.predic8.membrane.core.lang.ExchangeExpression.Language.*;
29-
3022
public abstract class AbstractServiceProxy extends SSLableProxy {
3123

3224
@Override
@@ -89,167 +81,6 @@ public void setPath(Path path) {
8981
}
9082
}
9183

92-
/**
93-
* @description <p>
94-
* The destination where the service proxy will send messages to.
95-
* Use the target element if you want to send the messages to a target.
96-
* Supports dynamic destinations through expressions.
97-
* </p>
98-
*/
99-
@MCElement(name = "target", component = false)
100-
public static class Target implements XMLSupport {
101-
private String host;
102-
private int port = -1;
103-
private String method;
104-
protected String url;
105-
private boolean adjustHostHeader = true;
106-
private ExchangeExpression.Language language = SPEL;
107-
private ExchangeExpression exchangeExpression;
108-
109-
private SSLParser sslParser;
110-
111-
protected XmlConfig xmlConfig;
112-
113-
public void init(Router router) {
114-
if (url != null) {
115-
exchangeExpression = TemplateExchangeExpression.newInstance(new InterceptorAdapter(router, xmlConfig), language, url);
116-
}
117-
118-
}
119-
120-
public String compileUrl(Exchange exc, Interceptor.Flow flow) {
121-
/*
122-
* Will always evaluate on every call. This is fine as SpEL is fast enough and performs its own optimizations.
123-
* 1.000.000 calls ~10ms
124-
*/
125-
if (exchangeExpression != null) {
126-
return exchangeExpression.evaluate(exc, flow, String.class);
127-
}
128-
return url;
129-
}
130-
131-
public Target() {
132-
}
133-
134-
public Target(String host) {
135-
setHost(host);
136-
}
137-
138-
public Target(String host, int port) {
139-
setHost(host);
140-
setPort(port);
141-
}
142-
143-
public String getHost() {
144-
return host;
145-
}
146-
147-
/**
148-
* @description Host address of the target.
149-
* @example localhost, 192.168.1.1
150-
*/
151-
@MCAttribute
152-
public void setHost(String host) {
153-
this.host = host;
154-
}
155-
156-
public int getPort() {
157-
return port;
158-
}
159-
160-
/**
161-
* @description Port number of the target.
162-
* @default 80
163-
* @example 8080
164-
*/
165-
@MCAttribute
166-
public void setPort(int port) {
167-
this.port = port;
168-
}
169-
170-
public String getUrl() {
171-
return url;
172-
}
173-
174-
/**
175-
* @description Absolute URL of the target. If this is set, <i>host</i> and <i>port</i> will be ignored.
176-
* Supports inline expressions through <code>${&lt;expression&gt;}</code> elements.
177-
* @example <a href="http://membrane-soa.org">http://membrane-soa.org</a>
178-
*/
179-
@MCAttribute
180-
public void setUrl(String url) {
181-
this.url = url;
182-
}
183-
184-
public SSLParser getSslParser() {
185-
return sslParser;
186-
}
187-
188-
/**
189-
* @description Configures outbound SSL (HTTPS).
190-
*/
191-
@MCChildElement(allowForeign = true)
192-
public void setSslParser(SSLParser sslParser) {
193-
this.sslParser = sslParser;
194-
}
195-
196-
public boolean isAdjustHostHeader() {
197-
return adjustHostHeader;
198-
}
199-
200-
@MCAttribute
201-
public void setAdjustHostHeader(boolean adjustHostHeader) {
202-
this.adjustHostHeader = adjustHostHeader;
203-
}
204-
205-
public String getMethod() {
206-
return method;
207-
}
208-
209-
/**
210-
* @description The method that should be used to make the call to the backend.
211-
* Overwrites the original method.
212-
* @param method
213-
*/
214-
@MCAttribute
215-
public void setMethod(String method) {
216-
this.method = method;
217-
}
218-
219-
public ExchangeExpression getExchangeExpression() {
220-
return exchangeExpression;
221-
}
222-
223-
public ExchangeExpression.Language getLanguage() {
224-
return language;
225-
}
226-
227-
/**
228-
* @description the language of the inline expressions
229-
* @default SpEL
230-
* @example SpEL, groovy, jsonpath, xpath
231-
*/
232-
@MCAttribute
233-
public void setLanguage(ExchangeExpression.Language language) {
234-
this.language = language;
235-
}
236-
237-
/**
238-
* XML Configuration e.g. declaration of XML namespaces for XPath expressions, ...
239-
* @param xmlConfig
240-
*/
241-
@Override
242-
@MCChildElement(allowForeign = true,order = 10)
243-
public void setXmlConfig(XmlConfig xmlConfig) {
244-
this.xmlConfig = xmlConfig;
245-
}
246-
247-
@Override
248-
public XmlConfig getXmlConfig() {
249-
return xmlConfig;
250-
}
251-
}
252-
25384
protected Target target = new Target();
25485

25586
public Target getTarget() {

core/src/main/java/com/predic8/membrane/core/proxies/RuleManager.java

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -121,25 +121,32 @@ public synchronized void openPorts() throws IOException {
121121
}
122122

123123
private @NotNull HashMap<IpPort, SSLContextCollection.Builder> getSSLContexts() throws UnknownHostException {
124-
HashMap<IpPort, SSLContextCollection.Builder> sslContexts = new HashMap<>();
124+
HashMap<IpPort, SSLContextCollection.Builder> sslContextBuilders = new HashMap<>();
125125
for (Proxy proxy : proxies) {
126-
if (!(proxy instanceof SSLableProxy sp))
127-
continue;
128-
129-
SSLContext sslContext = sp.getSslInboundContext();
130-
if (sslContext == null)
131-
continue;
132-
133-
IpPort ipPort = getIpPort(sp);
134-
SSLContextCollection.Builder builder = sslContexts.get(ipPort);
135-
if (builder == null) {
136-
builder = new SSLContextCollection.Builder();
137-
sslContexts.put(ipPort, builder);
126+
switch (proxy) {
127+
case SSLProxy sslp:
128+
getOrCreateBuilder(sslp, sslContextBuilders).useCollection();
129+
break;
130+
case SSLableProxy sslap:
131+
SSLContext sslContext = sslap.getSslInboundContext();
132+
if (sslContext == null)
133+
continue;
134+
getOrCreateBuilder(sslap, sslContextBuilders).add(sslContext);
135+
break;
136+
default: break;
138137
}
139-
builder.add(sslContext);
138+
}
139+
return sslContextBuilders;
140+
}
140141

142+
private static SSLContextCollection.@NotNull Builder getOrCreateBuilder(Proxy proxy, HashMap<IpPort, SSLContextCollection.Builder> sslContexts) throws UnknownHostException {
143+
IpPort ipPort = getIpPort(proxy);
144+
SSLContextCollection.Builder builder = sslContexts.get(ipPort);
145+
if (builder == null) {
146+
builder = new SSLContextCollection.Builder();
147+
sslContexts.put(ipPort, builder);
141148
}
142-
return sslContexts;
149+
return builder;
143150
}
144151

145152

core/src/main/java/com/predic8/membrane/core/proxies/SSLProxy.java

Lines changed: 33 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,50 +15,49 @@
1515
package com.predic8.membrane.core.proxies;
1616

1717
import com.google.common.base.Objects;
18-
import com.predic8.membrane.annot.MCAttribute;
19-
import com.predic8.membrane.annot.MCChildElement;
20-
import com.predic8.membrane.annot.MCElement;
21-
import com.predic8.membrane.core.config.security.SSLParser;
22-
import com.predic8.membrane.core.exchange.Exchange;
18+
import com.predic8.membrane.annot.*;
19+
import com.predic8.membrane.core.config.security.*;
20+
import com.predic8.membrane.core.exchange.*;
2321
import com.predic8.membrane.core.interceptor.*;
2422
import com.predic8.membrane.core.router.*;
25-
import com.predic8.membrane.core.sslinterceptor.SSLInterceptor;
26-
import com.predic8.membrane.core.stats.RuleStatisticCollector;
23+
import com.predic8.membrane.core.sslinterceptor.*;
24+
import com.predic8.membrane.core.stats.*;
2725
import com.predic8.membrane.core.transport.http.*;
28-
import com.predic8.membrane.core.transport.http.client.ConnectionConfiguration;
26+
import com.predic8.membrane.core.transport.http.client.*;
2927
import com.predic8.membrane.core.transport.http.streampump.*;
30-
import com.predic8.membrane.core.transport.ssl.SSLContext;
31-
import com.predic8.membrane.core.transport.ssl.SSLExchange;
32-
import com.predic8.membrane.core.transport.ssl.SSLProvider;
33-
import com.predic8.membrane.core.transport.ssl.StaticSSLContext;
34-
import com.predic8.membrane.core.util.DNSCache;
35-
import org.slf4j.Logger;
36-
import org.slf4j.LoggerFactory;
37-
import com.predic8.membrane.annot.Required;
38-
39-
import java.io.IOException;
40-
import java.net.InetAddress;
41-
import java.net.Socket;
42-
import java.net.SocketException;
43-
import java.util.ArrayList;
44-
import java.util.List;
45-
46-
import static com.predic8.membrane.core.interceptor.FlowController.ABORTION_REASON;
28+
import com.predic8.membrane.core.transport.ssl.*;
29+
import com.predic8.membrane.core.util.*;
30+
import org.slf4j.*;
31+
32+
import java.io.*;
33+
import java.net.*;
34+
import java.util.*;
35+
36+
import static com.predic8.membrane.core.interceptor.FlowController.*;
4737

4838
/**
4939
* Proxies SSL connections to a target server without decrypting the traffic.
5040
*/
51-
@MCElement(name="sslProxy")
41+
@MCElement(name = "sslProxy", topLevel = true, component = false)
5242
public class SSLProxy implements Proxy {
5343
private static final Logger log = LoggerFactory.getLogger(SSLProxy.class.getName());
5444

55-
private Target target;
45+
private SSLProxy.Target target;
5646
private ConnectionConfiguration connectionConfiguration = new ConnectionConfiguration();
5747
private final RuleStatisticCollector ruleStatisticCollector = new RuleStatisticCollector();
5848
private boolean useAsDefault = true;
5949
private List<SSLInterceptor> sslInterceptors = new ArrayList<>();
6050

61-
@MCElement(id = "sslProxy-target", name="target", component = false)
51+
public ConnectionConfiguration getConnectionConfiguration() {
52+
return connectionConfiguration;
53+
}
54+
55+
@MCChildElement(order = 0)
56+
public void setConnectionConfiguration(ConnectionConfiguration connectionConfiguration) {
57+
this.connectionConfiguration = connectionConfiguration;
58+
}
59+
60+
@MCElement(id = "sslProxy-target", name = "target", component = false)
6261
public static class Target {
6362
private int port = -1;
6463
private String host;
@@ -82,22 +81,13 @@ public void setHost(String host) {
8281
}
8382
}
8483

85-
public ConnectionConfiguration getConnectionConfiguration() {
86-
return connectionConfiguration;
87-
}
88-
89-
@MCChildElement(order = 0)
90-
public void setConnectionConfiguration(ConnectionConfiguration connectionConfiguration) {
91-
this.connectionConfiguration = connectionConfiguration;
92-
}
93-
94-
public Target getTarget() {
84+
public SSLProxy.Target getTarget() {
9585
return target;
9686
}
9787

9888
@Required
9989
@MCChildElement(order = 100)
100-
public void setTarget(Target target) {
90+
public void setTarget(SSLProxy.Target target) {
10191
this.target = target;
10292
}
10393

@@ -115,7 +105,7 @@ public List<SSLInterceptor> getSslInterceptors() {
115105
return sslInterceptors;
116106
}
117107

118-
@MCChildElement(allowForeign=true, order=50)
108+
@MCChildElement(allowForeign = true, order = 50)
119109
public void setSslInterceptors(List<SSLInterceptor> sslInterceptors) {
120110
this.sslInterceptors = sslInterceptors;
121111
}
@@ -174,7 +164,7 @@ public void setName(String name) {
174164

175165
@Override
176166
public String getName() {
177-
return "SSL " + getHost() + ":" + getPort();
167+
return "SSL %s:%d".formatted(getHost(), getPort());
178168
}
179169

180170
@Override
@@ -361,8 +351,8 @@ public Socket wrap(Socket socket, byte[] buffer, int position) throws IOExceptio
361351
log.error("", (Throwable) exc.getProperty(ABORTION_REASON));
362352
byte error = exc.getError().getCode();
363353

364-
byte[] alert_unrecognized_name = { 21 /* alert */, 3, 1 /* TLS 1.0 */, 0, 2 /* length: 2 bytes */,
365-
2 /* fatal */, error };
354+
byte[] alert_unrecognized_name = {21 /* alert */, 3, 1 /* TLS 1.0 */, 0, 2 /* length: 2 bytes */,
355+
2 /* fatal */, error};
366356

367357
try (socket) {
368358
socket.getOutputStream().write(alert_unrecognized_name);

0 commit comments

Comments
 (0)