Skip to content
This repository has been archived by the owner on Mar 31, 2024. It is now read-only.

Commit

Permalink
Agent: Sanitize logs with hosts to prevent pwd leakage
Browse files Browse the repository at this point in the history
  • Loading branch information
bleskes committed Nov 15, 2014
1 parent 38cc8ae commit e178d49
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 26 deletions.
8 changes: 8 additions & 0 deletions agent/src/main/java/org/elasticsearch/marvel/agent/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,12 @@ public static int parseIndexVersionFromTemplate(byte[] template) throws Unsuppor
return -1;
}
}

private static final String userInfoChars = "\\w-\\._~!$&\\'\\(\\)*+,;=%";
private static Pattern urlPwdSanitizer = Pattern.compile("([" + userInfoChars + "]+?):[" + userInfoChars + "]+?@");

public static String santizeUrlPwds(Object text) {
Matcher matcher = urlPwdSanitizer.matcher(text.toString());
return matcher.replaceAll("$1:XXXXXX@");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package org.elasticsearch.marvel.agent.exporter;

import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.admin.cluster.node.stats.NodeStats;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
import org.elasticsearch.action.admin.indices.stats.CommonStats;
Expand Down Expand Up @@ -110,15 +111,7 @@ public ESExporter(Settings settings, ClusterService clusterService, ClusterName

hosts = settings.getAsArray(SETTINGS_HOSTS, Strings.EMPTY_ARRAY);

for (String host : hosts) {
try {
Utils.parseHostWithPath(host, "");
} catch (URISyntaxException e) {
throw new RuntimeException("[marvel.agent.exporter] invalid host: [" + host + "]. error: [" + e.getMessage() + "]");
} catch (MalformedURLException e) {
throw new RuntimeException("[marvel.agent.exporter] invalid host: [" + host + "]. error: [" + e.getMessage() + "]");
}
}
validateHosts(hosts);

indexPrefix = settings.get(SETTINGS_INDEX_PREFIX, ".marvel");
String indexTimeFormat = settings.get(SETTINGS_INDEX_TIME_FORMAT, "YYYY.MM.dd");
Expand Down Expand Up @@ -149,7 +142,22 @@ public ESExporter(Settings settings, ClusterService clusterService, ClusterName
sslSocketFactory = null;
}

logger.debug("initialized with targets: {}, index prefix [{}], index time format [{}]", hosts, indexPrefix, indexTimeFormat);
logger.debug("initialized with targets: {}, index prefix [{}], index time format [{}]",
Utils.santizeUrlPwds(Strings.arrayToCommaDelimitedString(hosts)), indexPrefix, indexTimeFormat);
}

static private void validateHosts(String[] hosts) {
for (String host : hosts) {
try {
Utils.parseHostWithPath(host, "");
} catch (URISyntaxException e) {
throw new RuntimeException("[marvel.agent.exporter] invalid host: [" + Utils.santizeUrlPwds(host) + "]." +
" error: [" + Utils.santizeUrlPwds(e.getMessage()) + "]");
} catch (MalformedURLException e) {
throw new RuntimeException("[marvel.agent.exporter] invalid host: [" + Utils.santizeUrlPwds(host) + "]." +
" error: [" + Utils.santizeUrlPwds(e.getMessage()) + "]");
}
}
}

@Override
Expand Down Expand Up @@ -189,7 +197,8 @@ public void exportIndicesStats(IndicesStatsResponse indicesStats) {
addXContentRendererToConnection(conn, indicesStatsRenderer);
sendCloseExportingConnection(conn);
} catch (IOException e) {
logger.error("error sending data to [{}]", e, conn.getURL());
logger.error("error sending data to [{}]: [{}]", Utils.santizeUrlPwds(conn.getURL()),
Utils.santizeUrlPwds(ExceptionsHelper.detailedMessage(e)));
return;
}
}
Expand Down Expand Up @@ -281,8 +290,7 @@ private void exportXContent(MultiXContentRenderer xContentRenderer) {
addXContentRendererToConnection(conn, xContentRenderer);
sendCloseExportingConnection(conn);
} catch (IOException e) {
logger.error("error sending data to [{}]", e, conn.getURL());
return;
logger.error("error sending data to [{}]: {}", Utils.santizeUrlPwds(conn.getURL()), Utils.santizeUrlPwds(ExceptionsHelper.detailedMessage(e)));
}

}
Expand Down Expand Up @@ -385,11 +393,11 @@ private HttpURLConnection openAndValidateConnection(String method, String path,
System.arraycopy(hosts, hostIndex, newHosts, 0, hosts.length - hostIndex);
System.arraycopy(hosts, 0, newHosts, hosts.length - hostIndex, hostIndex);
hosts = newHosts;
logger.debug("preferred target host is now [{}]", hosts[0]);
logger.debug("preferred target host is now [{}]", Utils.santizeUrlPwds(hosts[0]));
}
}

logger.error("could not connect to any configured elasticsearch instances: [{}]", hosts);
logger.error("could not connect to any configured elasticsearch instances: [{}]", Utils.santizeUrlPwds(Strings.arrayToCommaDelimitedString(hosts)));

return null;

Expand Down Expand Up @@ -424,17 +432,17 @@ private HttpURLConnection openConnection(String host, String method, String path

return conn;
} catch (URISyntaxException e) {
logErrorBasedOnLevel(e, "error parsing host [{}]", host);
logErrorBasedOnLevel(e, "error parsing host [{}]", Utils.santizeUrlPwds(host));
} catch (IOException e) {
logErrorBasedOnLevel(e, "error connecting to [{}]", host);
logErrorBasedOnLevel(e, "error connecting to [{}]", Utils.santizeUrlPwds(host));
}
return null;
}

private void logErrorBasedOnLevel(Throwable t, String msg, Object... params) {
logger.error(msg + " [" + t.getMessage() + "]", params);
logger.error(msg + " [" + Utils.santizeUrlPwds(t.getMessage()) + "]", params);
if (logger.isDebugEnabled()) {
logger.debug(msg + ". full error details", t, params);
logger.debug(msg + ". full error details:\n[{}]", params, Utils.santizeUrlPwds(ExceptionsHelper.detailedMessage(t)));
}
}

Expand Down Expand Up @@ -465,7 +473,6 @@ private boolean checkAndUploadIndexTemplate(final String host) {
if (expectedVersion < 0) {
throw new RuntimeException("failed to find an index version in pre-configured index template");
}
logger.trace("verifying template via [{}]", host);
HttpURLConnection conn = openConnection(host, "GET", "_template/marvel", null);
if (conn == null) {
return false;
Expand Down Expand Up @@ -504,7 +511,7 @@ private boolean checkAndUploadIndexTemplate(final String host) {

return hasTemplate;
} catch (IOException e) {
logger.error("failed to verify/upload the marvel template to [{}]", e, host);
logger.error("failed to verify/upload the marvel template to [{}]:\n{}", Utils.santizeUrlPwds(host), Utils.santizeUrlPwds(e.getMessage()));
return false;
}
}
Expand All @@ -518,9 +525,12 @@ private void logConnectionError(String msg, HttpURLConnection conn) {
}

try {
logger.error("{} response code [{} {}]. content: [{}]", msg, conn.getResponseCode(), conn.getResponseMessage(), err);
logger.error("{} response code [{} {}]. content: [{}]",
Utils.santizeUrlPwds(msg), conn.getResponseCode(),
Utils.santizeUrlPwds(conn.getResponseMessage()),
Utils.santizeUrlPwds(err));
} catch (IOException e) {
logger.error("{}. connection had an error while reporting the error. tough life.", msg);
logger.error("{}. connection had an error while reporting the error. tough life.", Utils.santizeUrlPwds(msg));
}
}

Expand All @@ -540,7 +550,7 @@ public void onRefreshSettings(Settings settings) {

String[] newHosts = settings.getAsArray(SETTINGS_HOSTS, null);
if (newHosts != null) {
logger.info("hosts set to [{}]", Strings.arrayToCommaDelimitedString(newHosts));
logger.info("hosts set to [{}]", Utils.santizeUrlPwds(Strings.arrayToCommaDelimitedString(newHosts)));
this.hosts = newHosts;
this.checkedAndUploadedIndexTemplate = false;
this.boundToLocalNode = false;
Expand Down Expand Up @@ -803,15 +813,16 @@ public void run() {
}
HttpURLConnection conn = openConnection(currentHosts[0], "GET", "", null);
if (conn == null) {
logger.trace("keep alive thread shutting down. failed to open connection to current host [{}]", currentHosts[0]);
logger.trace("keep alive thread shutting down. failed to open connection to current host [{}]", Utils.santizeUrlPwds(currentHosts[0]));
return;
} else {
conn.getInputStream().close(); // close and release to connection pool.
}
} catch (InterruptedException e) {
// ignore, if closed, good....
} catch (Throwable t) {
logger.debug("error in keep alive thread, shutting down (will be restarted after a successful connection has been made)", t);
logger.debug("error in keep alive thread, shutting down (will be restarted after a successful connection has been made) {}",
Utils.santizeUrlPwds(ExceptionsHelper.detailedMessage(t)));
return;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;


public class UtilsUnitTests extends ElasticsearchTestCase {
Expand Down Expand Up @@ -139,4 +143,47 @@ void verifyUrl(URL url, String protocol, String host, int port, String path, Str
assertThat(url.getUserInfo(), equalTo(userInfo));

}

@Test
public void sanitizeUrlPadTest() throws UnsupportedEncodingException {
String pwd = URLEncoder.encode(randomRealisticUnicodeOfCodepointLengthBetween(3, 20), "UTF-8");
String[] inputs = new String[]{
"https://boaz:" + pwd + "@hostname:9200",
"http://boaz:" + pwd + "@hostname:9200",
"boaz:" + pwd + "@hostname",
"boaz:" + pwd + "@hostname/hello",
"Parse exception in [boaz:" + pwd + "@hostname:9200,boaz1:" + pwd + "@hostname \n" +
"caused: by exception ,boaz1:" + pwd + "@hostname",
"failed to upload index template, stopping export\n" +
"java.lang.RuntimeException: failed to load/verify index template\n" +
" at org.elasticsearch.marvel.agent.exporter.ESExporter.checkAndUploadIndexTemplate(ESExporter.java:525)\n" +
" at org.elasticsearch.marvel.agent.exporter.ESExporter.openExportingConnection(ESExporter.java:213)\n" +
" at org.elasticsearch.marvel.agent.exporter.ESExporter.exportXContent(ESExporter.java:285)\n" +
" at org.elasticsearch.marvel.agent.exporter.ESExporter.exportClusterStats(ESExporter.java:206)\n" +
" at org.elasticsearch.marvel.agent.AgentService$ExportingWorker.exportClusterStats(AgentService.java:288)\n" +
" at org.elasticsearch.marvel.agent.AgentService$ExportingWorker.run(AgentService.java:245)\n" +
" at java.lang.Thread.run(Thread.java:745)\n" +
"Caused by: java.io.IOException: Server returned HTTP response code: 401 for URL: http://marvel_exporter:" + pwd + "@localhost:9200/_template/marvel\n" +
" at sun.reflect.GeneratedConstructorAccessor3.newInstance(Unknown Source)\n" +
" at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)\n" +
" at java.lang.reflect.Constructor.newInstance(Constructor.java:526)\n" +
" at sun.net.www.protocol.http.HttpURLConnection$6.run(HttpURLConnection.java:1675)\n" +
" at sun.net.www.protocol.http.HttpURLConnection$6.run(HttpURLConnection.java:1673)\n" +
" at java.security.AccessController.doPrivileged(Native Method)\n" +
" at sun.net.www.protocol.http.HttpURLConnection.getChainedException(HttpURLConnection.java:1671)\n" +
" at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1244)\n" +
" at org.elasticsearch.marvel.agent.exporter.ESExporter.checkAndUploadIndexTemplate(ESExporter.java:519)\n" +
" ... 6 more\n" +
"Caused by: java.io.IOException: Server returned HTTP response code: 401 for URL: http://marvel_exporter:" + pwd + "@localhost:9200/_template/marvel\n" +
" at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1626)\n" +
" at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:468)\n" +
" at org.elasticsearch.marvel.agent.exporter.ESExporter.checkAndUploadIndexTemplate(ESExporter.java:514)\n" +
" ... 6 more"
};

for (String input : inputs) {
String sanitized = Utils.santizeUrlPwds(input);
assertThat(sanitized, not(containsString(pwd)));
}
}
}

0 comments on commit e178d49

Please sign in to comment.