Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unsecure ssl support #92

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ excludes:
- jvm.memory.pools.PS-Survivor-Space.usage
precision: 1m # only store time precision to the minute
prefix: ""
trustAllCerts: false
trustAllHostnames: false
database: ""
auth: ""
measurementMappings: {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@
* <td>The prefix for Metric key names (measurement) to report to InfluxDb.</td>
* </tr>
* <tr>
* <td>trustAllCerts</td>
* <td>false</td>
* <td>Whether we should trust all certs supplied by the server or not</td>
* </tr>
* <tr>
* <td>trustAllHostnames</td>
* <td>false</td>
* <td>Whether we should trust all hostnames supplied by the server or not</td>
* </tr>
* <tr>
* <td>tags</td>
* <td><i>None</i></td>
* <td>tags for all metrics reported to InfluxDb.</td>
Expand Down Expand Up @@ -164,6 +174,12 @@ public class InfluxDbReporterFactory extends BaseReporterFactory {

@NotNull
private String prefix = "";

@NotNull
private Boolean trustAllCerts = false;

@NotNull
private Boolean trustAllHostnames = false;

@NotNull
private Map<String, String> tags = new HashMap<>();
Expand Down Expand Up @@ -283,6 +299,26 @@ public String getPrefix() {
public void setPrefix(String prefix) {
this.prefix = prefix;
}

@JsonProperty
public Boolean isTrustAllCerts() {
return this.trustAllCerts;
}

@JsonProperty
public void setTrustAllCerts(Boolean trustAllCerts) {
this.trustAllCerts = trustAllCerts;
}

@JsonProperty
public Boolean isTrustAllHostnames() {
return this.trustAllHostnames;
}

@JsonProperty
public void setTrustAllHostnames(Boolean trustAllHostnames) {
this.trustAllHostnames = trustAllHostnames;
}

@JsonProperty
private Map<String, String> getTags() {
Expand Down Expand Up @@ -433,7 +469,9 @@ public ScheduledReporter build(MetricRegistry registry) {
precision.getUnit(),
connectTimeout,
readTimeout,
prefix
prefix,
trustAllCerts,
trustAllHostnames
)
);
case TCP:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

Expand Down Expand Up @@ -152,6 +154,18 @@ protected InfluxDbReporter.Builder builder(MetricRegistry registry) {
assertThat(getField(influxDb, InfluxDbHttpSender.class, "connectTimeout")).isEqualTo(2000);
assertThat(getField(influxDb, InfluxDbHttpSender.class, "readTimeout")).isEqualTo(3000);
}

@Test
public void shouldChangeTrusts() throws Exception {
factory.setTrustAllCerts(true);
assertTrue(factory.isTrustAllCerts());
factory.setTrustAllHostnames(true);
assertTrue(factory.isTrustAllHostnames());
factory.setTrustAllCerts(false);
assertFalse(factory.isTrustAllCerts());
factory.setTrustAllHostnames(false);
assertFalse(factory.isTrustAllHostnames());
}

@Test
public void shouldBuildWithTcpSender() throws Exception {
Expand Down
8 changes: 8 additions & 0 deletions findbugs/excludes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,12 @@
<Class name="com.izettle.metrics.influxdb.InfluxDbTcpSender" />
<Bug pattern="UNENCRYPTED_SOCKET" />
</Match>
<Match>
<Package name="com.izettle.metrics.influxdb" />
<Bug pattern="WEAK_TRUST_MANAGER" />
</Match>
<Match>
<Package name="com.izettle.metrics.influxdb" />
<Bug pattern="WEAK_HOSTNAME_VERIFIER" />
</Match>
</FindBugsFilter>
Original file line number Diff line number Diff line change
Expand Up @@ -6,87 +6,169 @@
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.codec.binary.Base64;

/**
* An implementation of InfluxDbSender that writes to InfluxDb via http.
*/
public class InfluxDbHttpSender extends InfluxDbBaseSender {

private final URL url;
// The base64 encoded authString.
private final String authStringEncoded;
private final int connectTimeout;
private final int readTimeout;

/**
* Creates a new http sender given connection details.
*
* @param hostname the influxDb hostname
* @param port the influxDb http port
* @param database the influxDb database to write to
* @param authString the authorization string to be used to connect to InfluxDb, of format username:password
* @param timePrecision the time precision of the metrics
* @param connectTimeout the connect timeout
* @param connectTimeout the read timeout
* @throws Exception exception while creating the influxDb sender(MalformedURLException)
*/
public InfluxDbHttpSender(
final String protocol, final String hostname, final int port, final String database, final String authString,
final TimeUnit timePrecision, final int connectTimeout, final int readTimeout, final String measurementPrefix)
throws Exception {
super(database, timePrecision, measurementPrefix);

String endpoint = new URL(protocol, hostname, port, "/write").toString();
String queryDb = String.format("db=%s", URLEncoder.encode(database, "UTF-8"));
String queryPrecision = String.format("precision=%s", TimeUtils.toTimePrecision(timePrecision));
this.url = new URL(endpoint + "?" + queryDb + "&" + queryPrecision);

if (authString != null && !authString.isEmpty()) {
this.authStringEncoded = Base64.encodeBase64String(authString.getBytes(UTF_8));
} else {
this.authStringEncoded = "";
}

this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
}

@Deprecated
public InfluxDbHttpSender(
final String protocol, final String hostname, final int port, final String database, final String authString,
final TimeUnit timePrecision) throws Exception {
this(protocol, hostname, port, database, authString, timePrecision, 1000, 1000, "");
}

@Override
protected int writeData(byte[] line) throws Exception {
final HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
if (authStringEncoded != null && !authStringEncoded.isEmpty()) {
con.setRequestProperty("Authorization", "Basic " + authStringEncoded);
}
con.setDoOutput(true);
con.setConnectTimeout(connectTimeout);
con.setReadTimeout(readTimeout);

OutputStream out = con.getOutputStream();
try {
out.write(line);
out.flush();
} finally {
out.close();
}

int responseCode = con.getResponseCode();

// Check if non 2XX response code.
if (responseCode / 100 != 2) {
throw new IOException(
"Server returned HTTP response code: " + responseCode + " for URL: " + url + " with content :'"
+ con.getResponseMessage() + "'");
}
return responseCode;
}
private final URL url;
// The base64 encoded authString.
private final String authStringEncoded;
private final int connectTimeout;
private final int readTimeout;

/**
* Creates a new http sender given connection details.
*
* @param hostname
* the influxDb hostname
* @param port
* the influxDb http port
* @param database
* the influxDb database to write to
* @param authString
* the authorization string to be used to connect to InfluxDb, of
* format username:password
* @param timePrecision
* the time precision of the metrics
* @param connectTimeout
* the connect timeout
* @param connectTimeout
* the read timeout
* @param trustAllCerts
* whether all certs should be trusted or not (setting this to true may expose you to MITM attacks)
* @param trustAllHostnames
* whether all hostnames should be trusted or not (setting this to true may expose you to MITM attacks)
* @throws Exception
* exception while creating the influxDb
* sender(MalformedURLException)
*/
public InfluxDbHttpSender(final String protocol, final String hostname, final int port, final String database,
final String authString, final TimeUnit timePrecision, final int connectTimeout, final int readTimeout,
final String measurementPrefix, boolean trustAllCerts, boolean trustAllHostnames) throws Exception {
super(database, timePrecision, measurementPrefix);
updateSecurity(trustAllCerts, trustAllHostnames);
String endpoint = new URL(protocol, hostname, port, "/write").toString();
String queryDb = String.format("db=%s", URLEncoder.encode(database, "UTF-8"));
String queryPrecision = String.format("precision=%s", TimeUtils.toTimePrecision(timePrecision));
this.url = new URL(endpoint + "?" + queryDb + "&" + queryPrecision);

if (authString != null && !authString.isEmpty()) {
this.authStringEncoded = Base64.encodeBase64String(authString.getBytes(UTF_8));
} else {
this.authStringEncoded = "";
}

this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
}

private void updateSecurity(boolean trustAllCerts, boolean trustAllHostnames)
throws NoSuchAlgorithmException, KeyManagementException {
updateCertTrust(trustAllCerts);
updateHostnameTrust(trustAllHostnames);
}

public static class TrustingHostNameVerifier implements HostnameVerifier {

@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}

}

private void updateHostnameTrust(boolean trusting) {
if (trusting) {
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(new TrustingHostNameVerifier());
}
}

public static class TrustingX509TrustManager implements X509TrustManager {

@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}

@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}

@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[] {};
}

}

private void updateCertTrust(boolean trusting) throws NoSuchAlgorithmException, KeyManagementException {
if (trusting) {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { new TrustingX509TrustManager() };

// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}
}

@Deprecated
public InfluxDbHttpSender(final String protocol, final String hostname, final int port, final String database,
final String authString, final TimeUnit timePrecision, final int connectTimeout, final int readTimeout,
final String measurementPrefix) throws Exception {
this(protocol, hostname, port, database, authString, timePrecision, connectTimeout, readTimeout,
measurementPrefix, false, false);
}

@Deprecated
public InfluxDbHttpSender(final String protocol, final String hostname, final int port, final String database,
final String authString, final TimeUnit timePrecision) throws Exception {
this(protocol, hostname, port, database, authString, timePrecision, 1000, 1000, "");
}

@Override
protected int writeData(byte[] line) throws Exception {
final HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
if (authStringEncoded != null && !authStringEncoded.isEmpty()) {
con.setRequestProperty("Authorization", "Basic " + authStringEncoded);
}
con.setDoOutput(true);
con.setConnectTimeout(connectTimeout);
con.setReadTimeout(readTimeout);

OutputStream out = con.getOutputStream();
try {
out.write(line);
out.flush();
} finally {
out.close();
}

int responseCode = con.getResponseCode();

// Check if non 2XX response code.
if (responseCode / 100 != 2) {
throw new IOException("Server returned HTTP response code: " + responseCode + " for URL: " + url
+ " with content :'" + con.getResponseMessage() + "'");
}
return responseCode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@ public void shouldThrowUnknownHostException() throws Exception {
80,
"testdb",
"asdf",
TimeUnit.MINUTES,
1000,
1000,
""
TimeUnit.MINUTES
);
influxDbHttpSender.writeData(new byte[0]);
}
Expand All @@ -51,9 +48,11 @@ public void shouldThrowConnectException() throws Exception {
"testdb",
"asdf",
TimeUnit.MINUTES,
1000,
1000,
""
0,
0,
"",
true,
true
);
influxDbHttpSender.writeData(new byte[0]);
}
Expand Down