Test: remove use of network.host in smoke test ssl plugins

This removes the use of a specific address in smoke test ssl plugins and instead generates
the certificate with all of the IP addresses and DNS names of the system as subject
alternative names. This required duplication and modification of some code from core's
NetworkUtils.

Original commit: elastic/x-pack-elasticsearch@576824376f
This commit is contained in:
jaymode 2016-02-23 17:12:09 -08:00
parent 2f088a60bc
commit 0522127924
1 changed files with 147 additions and 6 deletions

View File

@ -7,9 +7,6 @@ dependencies {
testCompile project(path: ':x-plugins:elasticsearch:x-pack', configuration: 'runtime') testCompile project(path: ':x-plugins:elasticsearch:x-pack', configuration: 'runtime')
} }
// needed to be consistent with ssl host checking
String host = InetAddress.getLoopbackAddress().getHostAddress();
// location of keystore and files to generate it // location of keystore and files to generate it
File keystore = new File(project.buildDir, 'keystore/test-node.jks') File keystore = new File(project.buildDir, 'keystore/test-node.jks')
@ -19,6 +16,8 @@ task createKey(type: LoggedExec) {
project.delete(keystore.parentFile) project.delete(keystore.parentFile)
keystore.parentFile.mkdirs() keystore.parentFile.mkdirs()
} }
// needed to be consistent with ssl host checking
String san = getSubjectAlternativeNameString()
executable = 'keytool' executable = 'keytool'
standardInput = new ByteArrayInputStream('FirstName LastName\nUnit\nOrganization\nCity\nState\nNL\nyes\n\n'.getBytes('UTF-8')) standardInput = new ByteArrayInputStream('FirstName LastName\nUnit\nOrganization\nCity\nState\nNL\nyes\n\n'.getBytes('UTF-8'))
args '-genkey', args '-genkey',
@ -27,9 +26,10 @@ task createKey(type: LoggedExec) {
'-keyalg', 'RSA', '-keyalg', 'RSA',
'-keysize', '2048', '-keysize', '2048',
'-validity', '712', '-validity', '712',
'-dname', 'CN=' + host, '-dname', 'CN=smoke-test-plugins-ssl',
'-keypass', 'keypass', '-keypass', 'keypass',
'-storepass', 'keypass' '-storepass', 'keypass',
'-ext', san
} }
// add keystore to test classpath: it expects it there // add keystore to test classpath: it expects it there
@ -45,7 +45,6 @@ project.rootProject.subprojects.findAll { it.path.startsWith(':plugins:') }.each
integTest { integTest {
cluster { cluster {
systemProperty 'es.network.host', host
systemProperty 'es.xpack.monitoring.agent.exporters.es.type', 'http' systemProperty 'es.xpack.monitoring.agent.exporters.es.type', 'http'
systemProperty 'es.xpack.monitoring.agent.exporters.es.enabled', 'false' systemProperty 'es.xpack.monitoring.agent.exporters.es.enabled', 'false'
systemProperty 'es.xpack.monitoring.agent.exporters.es.ssl.truststore.path', keystore.name systemProperty 'es.xpack.monitoring.agent.exporters.es.ssl.truststore.path', keystore.name
@ -80,3 +79,145 @@ processTestResources {
MavenFilteringHack.filter(it, expansions) MavenFilteringHack.filter(it, expansions)
} }
} }
// Code stolen from NetworkUtils/InetAddresses/NetworkAddress to support SAN
/** Return all interfaces (and subinterfaces) on the system */
static List<NetworkInterface> getInterfaces() throws SocketException {
List<NetworkInterface> all = new ArrayList<>();
addAllInterfaces(all, Collections.list(NetworkInterface.getNetworkInterfaces()));
Collections.sort(all, new Comparator<NetworkInterface>() {
@Override
public int compare(NetworkInterface left, NetworkInterface right) {
return Integer.compare(left.getIndex(), right.getIndex());
}
});
return all;
}
/** Helper for getInterfaces, recursively adds subinterfaces to {@code target} */
private static void addAllInterfaces(List<NetworkInterface> target, List<NetworkInterface> level) {
if (!level.isEmpty()) {
target.addAll(level);
for (NetworkInterface intf : level) {
addAllInterfaces(target, Collections.list(intf.getSubInterfaces()));
}
}
}
private static String getSubjectAlternativeNameString() {
List<InetAddress> list = new ArrayList<>();
for (NetworkInterface intf : getInterfaces()) {
if (intf.isUp()) {
// NOTE: some operating systems (e.g. BSD stack) assign a link local address to the loopback interface
// while technically not a loopback address, some of these treat them as one (e.g. OS X "localhost") so we must too,
// otherwise things just won't work out of box. So we include all addresses from loopback interfaces.
for (InetAddress address : Collections.list(intf.getInetAddresses())) {
if (intf.isLoopback() || address.isLoopbackAddress()) {
list.add(address);
}
}
}
}
if (list.isEmpty()) {
throw new IllegalArgumentException("no up-and-running loopback addresses found, got " + getInterfaces());
}
StringBuilder builder = new StringBuilder("san=");
for (int i = 0; i < list.size(); i++) {
InetAddress address = list.get(i);
String hostAddress;
if (address instanceof Inet6Address) {
hostAddress = compressedIPV6Address((Inet6Address)address);
} else {
hostAddress = address.getHostAddress();
}
builder.append("ip:").append(hostAddress);
String hostname = address.getHostName();
if (hostname.equals(address.getHostAddress()) == false) {
builder.append(",dns:").append(hostname);
}
if (i != (list.size() - 1)) {
builder.append(",");
}
}
return builder.toString();
}
private static String compressedIPV6Address(Inet6Address inet6Address) {
byte[] bytes = inet6Address.getAddress();
int[] hextets = new int[8];
for (int i = 0; i < hextets.length; i++) {
hextets[i] = (bytes[2 * i] & 255) << 8 | bytes[2 * i + 1] & 255;
}
compressLongestRunOfZeroes(hextets);
return hextetsToIPv6String(hextets);
}
/**
* Identify and mark the longest run of zeroes in an IPv6 address.
*
* <p>Only runs of two or more hextets are considered. In case of a tie, the
* leftmost run wins. If a qualifying run is found, its hextets are replaced
* by the sentinel value -1.
*
* @param hextets {@code int[]} mutable array of eight 16-bit hextets
*/
private static void compressLongestRunOfZeroes(int[] hextets) {
int bestRunStart = -1;
int bestRunLength = -1;
int runStart = -1;
for (int i = 0; i < hextets.length + 1; i++) {
if (i < hextets.length && hextets[i] == 0) {
if (runStart < 0) {
runStart = i;
}
} else if (runStart >= 0) {
int runLength = i - runStart;
if (runLength > bestRunLength) {
bestRunStart = runStart;
bestRunLength = runLength;
}
runStart = -1;
}
}
if (bestRunLength >= 2) {
Arrays.fill(hextets, bestRunStart, bestRunStart + bestRunLength, -1);
}
}
/**
* Convert a list of hextets into a human-readable IPv6 address.
*
* <p>In order for "::" compression to work, the input should contain negative
* sentinel values in place of the elided zeroes.
*
* @param hextets {@code int[]} array of eight 16-bit hextets, or -1s
*/
private static String hextetsToIPv6String(int[] hextets) {
/*
* While scanning the array, handle these state transitions:
* start->num => "num" start->gap => "::"
* num->num => ":num" num->gap => "::"
* gap->num => "num" gap->gap => ""
*/
StringBuilder buf = new StringBuilder(39);
boolean lastWasNumber = false;
for (int i = 0; i < hextets.length; i++) {
boolean thisIsNumber = hextets[i] >= 0;
if (thisIsNumber) {
if (lastWasNumber) {
buf.append(':');
}
buf.append(Integer.toHexString(hextets[i]));
} else {
if (i == 0 || lastWasNumber) {
buf.append("::");
}
}
lastWasNumber = thisIsNumber;
}
return buf.toString();
}