Skip to content

Commit 6e0a50d

Browse files
committed
adding standalone mode - addressing #6
1 parent 4f80aec commit 6e0a50d

File tree

11 files changed

+455
-8
lines changed

11 files changed

+455
-8
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,6 @@ SSL Support
8686
While the proxy supports SSL, it requires that a Certificate Authority be installed in to the browser / at client from where you call a server via the proxy.
8787
This allows the client to trust all the SSL traffic coming from the proxy, which will be proxied using a classic man-in-the-middle technique.
8888

89-
**IT IS CRITICAL THAT YOU NOT INSTALL THIS CERTIFICATE AUTHORITY ON A CLIENT/BROWSER THAT IS USED FOR ANYTHING OTHER THAN TESTING.**
89+
**IT IS CRITICAL THAT YOU DO NOT INSTALL THIS CERTIFICATE AUTHORITY ON A CLIENT/BROWSER THAT IS USED FOR ANYTHING OTHER THAN TESTING.**
90+
91+
It is recommended to use `-Djdk.tls.namedGroups="secp256r1, secp384r1, ffdhe2048, ffdhe3072"` java arguments to address some issues still existing in some JDK implementations.

build.gradle

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
/*==========================================================================
2+
Author: Tamas Kohegyi
3+
===========================================================================*/
4+
buildscript {
5+
dependencies {
6+
classpath 'gradle.plugin.com.github.jengelman.gradle.plugins:shadow:7.0.0'
7+
}
8+
}
9+
110
plugins {
211
id "org.sonarqube" version "3.2.0"
312
}
@@ -9,6 +18,15 @@ apply plugin: 'java-library'
918
apply plugin: 'maven-publish'
1019
apply plugin: 'signing'
1120
apply plugin: 'jacoco'
21+
apply plugin: 'application'
22+
apply plugin: 'com.github.johnrengelman.shadow'
23+
24+
repositories {
25+
mavenCentral()
26+
maven { url "https://repo.maven.apache.org/maven2" }
27+
maven { url "https://repository.mulesoft.org/nexus/content/repositories/public/" }
28+
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
29+
}
1230

1331
group = "website.magyar"
1432
archivesBaseName = "mitm-java-proxy"
@@ -19,16 +37,11 @@ ext.myBuildNumber='SNAPSHOT'
1937
if ( hasProperty('buildNumber') ) {
2038
myBuildNumber = "${project.ext.buildNumber}"
2139
}
22-
version = "$wilmaVersion" + ".24." + "${project.ext.myBuildNumber}"
40+
version = "$wilmaVersion" + ".25." + "${project.ext.myBuildNumber}"
41+
mainClassName = "website.magyar.mitm.standalone.StandaloneProxy"
2342

2443
def isSnapshot = project.version.contains('SNAPSHOT')
2544

26-
repositories {
27-
maven { url "https://repo.maven.apache.org/maven2" }
28-
maven { url "https://repository.mulesoft.org/nexus/content/repositories/public/" }
29-
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
30-
}
31-
3245
// Source and target info
3346
sourceCompatibility = JavaVersion.VERSION_15
3447
targetCompatibility = JavaVersion.VERSION_15
@@ -92,6 +105,34 @@ test {
92105

93106
test.finalizedBy jacocoTestReport
94107

108+
def configFileDefaultPath = "$project.projectDir/proxy.conf.properties"
109+
if (!hasProperty("configFilePath")) {
110+
project.ext.configFilePath = configFileDefaultPath
111+
}
112+
113+
run{
114+
args = ["$configFilePath"]
115+
}
116+
117+
shadowJar {
118+
append('META-INF/spring.handlers')
119+
append('META-INF/spring.schemas')
120+
121+
excludes = [
122+
'META-INF/*.SF',
123+
'META-INF/*.DSA',
124+
'META-INF/*.RSA',
125+
'images/',
126+
'*.txt',
127+
'*.gif',
128+
'*.png',
129+
'*.dtd',
130+
'build.properties',
131+
'XPP3_1.1.4c_MIN_VERSION',
132+
'activemq.xsd.*',
133+
'about.html'] as Iterable<String>
134+
}
135+
95136
dependencies {
96137
implementation group: 'ch.qos.logback', name: 'logback-classic', version:'1.2.11'
97138
implementation group: 'org.slf4j', name: 'jcl-over-slf4j', version:'1.7.36'
@@ -123,8 +164,34 @@ def myCopySpec = project.copySpec {
123164
}
124165

125166
jar {
167+
manifest.attributes( 'provider': 'gradle')
168+
manifest.attributes( 'Main-Class': mainClassName)
126169
manifest.attributes( 'Implementation-Title': "$componentDescription - V${project.version}")
127170
with myCopySpec
171+
finalizedBy shadowJar
172+
}
173+
174+
def myCopySpec2 = project.copySpec {
175+
from(rootProject.rootDir.absolutePath + '/build/libs/') {
176+
include "mitm-java-proxy-$version" + '-all.jar'
177+
}
178+
from(rootProject.rootDir.absolutePath + '/') {
179+
include 'LICENSE.txt'
180+
include 'README.txt'
181+
include 'README.md'
182+
}
183+
}
184+
185+
task copyToReleaseRoot (type: Copy) {
186+
dependsOn shadowJar
187+
into(rootProject.rootDir.absolutePath + '/release')
188+
rename "mitm-java-proxy-$version" + '-all.jar', "mitm-java-proxy-" + "$version" + ".jar"
189+
with myCopySpec2
190+
}
191+
192+
task release(type: Zip) {
193+
dependsOn copyToReleaseRoot
194+
from rootProject.rootDir.absolutePath + '/release'
128195
}
129196

130197
publishing {
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package website.magyar.mitm.standalone;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import website.magyar.mitm.proxy.ProxyServer;
6+
7+
import java.util.Properties;
8+
9+
/**
10+
* Bootstrap class that starts the application engine.
11+
*
12+
* @author Tamas Kohegyi
13+
*/
14+
public class Bootstrap {
15+
private static final int PROXY_TIMEOUT = 1200000; //20 minute - giving time to debug
16+
private final Logger logger = LoggerFactory.getLogger(Bootstrap.class);
17+
private int proxyPort = -1;
18+
19+
private Properties properties;
20+
private ProxyServer proxyServer;
21+
22+
public ProxyServer getProxyServer() {
23+
return proxyServer;
24+
}
25+
26+
public int getProxyPort() {
27+
return proxyPort;
28+
}
29+
30+
/**
31+
* Starts the application.
32+
*
33+
* @param args command line arguments
34+
*/
35+
public ProxyServer bootstrap(final String[] args) {
36+
ProxyServer proxyServer = null;
37+
PropertyLoader propertyLoader = new PropertyLoader();
38+
String configurationFile = args != null && args.length > 0 ? args[0] : null;
39+
properties = propertyLoader.loadProperties(configurationFile);
40+
Integer port = getPort();
41+
Integer proxyTimeout = getProxyTimeout();
42+
Boolean keepSslAlive = getKeepSslAlive();
43+
try {
44+
proxyServer = startProxy(port, proxyTimeout, keepSslAlive);
45+
} catch (Exception e) {
46+
e.printStackTrace();
47+
}
48+
return proxyServer;
49+
}
50+
51+
private Boolean getKeepSslAlive() {
52+
return Boolean.valueOf(properties.getProperty("proxy.keepSslAlive"));
53+
}
54+
55+
private Integer getProxyTimeout() {
56+
int timeout = PROXY_TIMEOUT;
57+
try {
58+
timeout = Integer.parseInt(properties.getProperty("proxy.timeout"));
59+
} catch (NumberFormatException e) {
60+
logger.warn("Invalid proxy timeout value! - Using default timeout:{}", PROXY_TIMEOUT);
61+
}
62+
return timeout;
63+
}
64+
65+
private Integer getPort() {
66+
int port = 0;
67+
try {
68+
port = Integer.parseInt(properties.getProperty("proxy.port"));
69+
} catch (NumberFormatException e) {
70+
logger.warn("Invalid proxy port value! - Using random port.");
71+
}
72+
return port;
73+
}
74+
75+
private ProxyServer startProxy(int port, int proxyTimeout, boolean keepSslAlive) throws Exception {
76+
proxyServer = new ProxyServer(port);
77+
proxyServer.start(proxyTimeout);
78+
proxyPort = proxyServer.getPort();
79+
ProxyServer.setShouldKeepSslConnectionAlive(keepSslAlive);
80+
return proxyServer;
81+
}
82+
83+
84+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package website.magyar.mitm.standalone;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import website.magyar.mitm.standalone.helper.PropertiesNotAvailableException;
6+
7+
import java.io.FileInputStream;
8+
import java.io.IOException;
9+
import java.io.InputStream;
10+
import java.util.Properties;
11+
12+
public class PropertyLoader {
13+
private final Logger logger = LoggerFactory.getLogger(PropertyLoader.class);
14+
15+
/**
16+
* Loads properties from the specified property file. Also validates
17+
* the property file.
18+
* @param configFile the configuration file to be read
19+
* @return the loaded {@link Properties}
20+
*/
21+
public Properties loadProperties(final String configFile) {
22+
Properties properties = new Properties();
23+
if (configFile != null) {
24+
try {
25+
checkPropertyFileArgument(configFile);
26+
InputStream inputStream = new FileInputStream(configFile);
27+
properties.load(inputStream);
28+
logger.debug("Properties loaded from external configuration.");
29+
} catch (IOException e) {
30+
throw new PropertiesNotAvailableException("Configuration file " + configFile + " cannot be loaded.");
31+
}
32+
}
33+
return properties;
34+
}
35+
36+
private void checkPropertyFileArgument(final String args) {
37+
if (args == null || "".equals(args)) {
38+
throw new PropertiesNotAvailableException("Configuration file was not specified as input argument!");
39+
} else if (!args.endsWith(".properties")) {
40+
throw new PropertiesNotAvailableException("Configuration file must be a properties file!");
41+
}
42+
}
43+
44+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package website.magyar.mitm.standalone;
2+
3+
public final class StandaloneProxy {
4+
public static String[] arguments; //NOSONAR
5+
6+
private StandaloneProxy() {
7+
}
8+
9+
/**
10+
* The app main entry point.
11+
* @param args The program needs the path of conf.properties to run.
12+
*/
13+
public static void main(final String[] args) {
14+
arguments = args; //NOSONAR
15+
new Bootstrap().bootstrap(args);
16+
}
17+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package website.magyar.mitm.standalone.helper;
2+
3+
/**
4+
* Thrown when the configuration properties cannot be loaded.
5+
* @author Tamas Kohegyi
6+
*
7+
*/
8+
public class InvalidPropertyException extends RuntimeException {
9+
10+
/**
11+
* Constructor with a cause.
12+
* @param message the message of the exception
13+
* @param throwable the cause of the exception
14+
*/
15+
public InvalidPropertyException(final String message, final Throwable throwable) {
16+
super(message, throwable);
17+
}
18+
19+
/**
20+
* Constructor with a message only.
21+
* @param message the message of the exception
22+
*/
23+
public InvalidPropertyException(final String message) {
24+
super(message);
25+
}
26+
27+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package website.magyar.mitm.standalone.helper;
2+
3+
/**
4+
* Thrown when the configuration properties cannot be loaded.
5+
* @author Tamas Kohegyi
6+
*
7+
*/
8+
public class PropertiesNotAvailableException extends RuntimeException {
9+
10+
/**
11+
* Constructor with a cause.
12+
* @param message the message of the exception
13+
* @param throwable the cause of the exception
14+
*/
15+
public PropertiesNotAvailableException(final String message, final Throwable throwable) {
16+
super(message, throwable);
17+
}
18+
19+
/**
20+
* Constructor with a message only.
21+
* @param message the message of the exception
22+
*/
23+
public PropertiesNotAvailableException(final String message) {
24+
super(message);
25+
}
26+
27+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#this property is just a sample file, to be used in standalone mode only
2+
proxy.port=9092
3+
proxy.timeout=30000
4+
proxy.keepSslAlive=false
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package website.magyar.mitm.proxy.stub;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import website.magyar.mitm.proxy.RequestInterceptor;
6+
import website.magyar.mitm.proxy.http.MitmJavaProxyHttpRequest;
7+
8+
import java.net.URI;
9+
import java.net.URISyntaxException;
10+
11+
/**
12+
* Class that is able to intercept and process every request going through the proxy, by implementing the RequestInterceptor interface.
13+
* It redirects all incoming message to the stub service - which acts as service virtualization.
14+
*
15+
* @author Tamas_Kohegyi
16+
*/
17+
public class RequestInterceptorForStub implements RequestInterceptor {
18+
19+
private final Logger logger = LoggerFactory.getLogger(RequestInterceptorForStub.class);
20+
21+
private URI stubUri;
22+
23+
public RequestInterceptorForStub(String stubUrl) {
24+
try {
25+
this.stubUri = new URI(stubUrl);
26+
} catch (URISyntaxException e) {
27+
this.stubUri = null;
28+
}
29+
}
30+
31+
public void process(final MitmJavaProxyHttpRequest request) {
32+
request.getMethod().setURI(stubUri);
33+
logger.info("Redirecting request:{} to stub service.", request.getMethod().getURI().getPath());
34+
}
35+
36+
}

0 commit comments

Comments
 (0)