Packaging: Include build information

Also added a ShieldBuild class to return

* Shield version
* Shield build hash
* Shield build timestamp

Also added a '/_shield' endpoint which returns those fields.

Original commit: elastic/x-pack-elasticsearch@38928d1ef6
This commit is contained in:
Alexander Reelsen 2014-11-05 10:25:54 +01:00
parent df3956fafe
commit 4903852f48
6 changed files with 207 additions and 4 deletions

18
pom.xml
View File

@ -430,6 +430,24 @@
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
<configuration>
<doCheck>false</doCheck>
<doUpdate>false</doUpdate>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@ -0,0 +1,83 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield;
import org.elasticsearch.common.io.FastStringReader;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.joda.time.DateTimeZone;
import org.elasticsearch.common.joda.time.format.ISODateTimeFormat;
import java.io.IOException;
import java.util.Properties;
/**
*
*/
public class ShieldBuild {
public static final ShieldBuild CURRENT;
static {
String hash = "NA";
String hashShort = "NA";
String timestamp = "NA";
try {
String properties = Streams.copyToStringFromClasspath("/shield-build.properties");
Properties props = new Properties();
props.load(new FastStringReader(properties));
hash = props.getProperty("hash", hash);
if (!hash.equals("NA")) {
hashShort = hash.substring(0, 7);
}
String gitTimestampRaw = props.getProperty("timestamp");
if (gitTimestampRaw != null) {
timestamp = ISODateTimeFormat.dateTimeNoMillis().withZone(DateTimeZone.UTC).print(Long.parseLong(gitTimestampRaw));
}
} catch (Exception e) {
// just ignore...
}
CURRENT = new ShieldBuild(hash, hashShort, timestamp);
}
private String hash;
private String hashShort;
private String timestamp;
ShieldBuild(String hash, String hashShort, String timestamp) {
this.hash = hash;
this.hashShort = hashShort;
this.timestamp = timestamp;
}
public String hash() {
return hash;
}
public String hashShort() {
return hashShort;
}
public String timestamp() {
return timestamp;
}
public static ShieldBuild readBuild(StreamInput in) throws IOException {
String hash = in.readString();
String hashShort = in.readString();
String timestamp = in.readString();
return new ShieldBuild(hash, hashShort, timestamp);
}
public static void writeBuild(ShieldBuild build, StreamOutput out) throws IOException {
out.writeString(build.hash());
out.writeString(build.hashShort());
out.writeString(build.timestamp());
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.http;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.*;
import org.elasticsearch.shield.ShieldBuild;
import org.elasticsearch.shield.ShieldVersion;
import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.HEAD;
import static org.elasticsearch.rest.RestStatus.OK;
public class RestShieldInfoAction extends BaseRestHandler {
@Inject
public RestShieldInfoAction(Settings settings, RestController controller, Client client) {
super(settings, controller, client);
controller.registerHandler(GET, "/_shield", this);
controller.registerHandler(HEAD, "/_shield", this);
}
@Override
protected void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception {
if (request.method() == RestRequest.Method.HEAD) {
channel.sendResponse(new BytesRestResponse(OK));
return;
}
XContentBuilder builder = channel.newBuilder();
// Default to pretty printing, but allow ?pretty=false to disable
if (!request.hasParam("pretty")) {
builder.prettyPrint().lfAtEnd();
}
builder.startObject();
if (settings.get("name") != null) {
builder.field("name", settings.get("name"));
}
builder.startObject("version")
.field("build_hash", ShieldBuild.CURRENT.hash())
.field("build_timestamp", ShieldBuild.CURRENT.timestamp())
.field("build_version", ShieldVersion.CURRENT.toString())
.endObject();
builder.field("tagline", "You know, for security");
builder.endObject();
channel.sendResponse(new BytesRestResponse(OK, builder));
}
}

View File

@ -12,10 +12,12 @@ import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.plugins.AbstractPlugin; import org.elasticsearch.plugins.AbstractPlugin;
import org.elasticsearch.rest.RestModule;
import org.elasticsearch.shield.ShieldModule; import org.elasticsearch.shield.ShieldModule;
import org.elasticsearch.shield.ShieldSettingsException; import org.elasticsearch.shield.ShieldSettingsException;
import org.elasticsearch.shield.authc.support.SecuredString; import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authc.support.UsernamePasswordToken; import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.shield.http.RestShieldInfoAction;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
@ -69,6 +71,11 @@ public class ShieldPlugin extends AbstractPlugin {
.put(setting, UsernamePasswordToken.basicAuthHeaderValue(username, new SecuredString(password.toCharArray()))).build(); .put(setting, UsernamePasswordToken.basicAuthHeaderValue(username, new SecuredString(password.toCharArray()))).build();
} }
public void onModule(RestModule restModule) {
restModule.addRestAction(RestShieldInfoAction.class);
}
public static Path configDir(Environment env) { public static Path configDir(Environment env) {
return new File(env.configFile(), NAME).toPath(); return new File(env.configFile(), NAME).toPath();
} }

View File

@ -0,0 +1,3 @@
version=${project.version}
hash=${buildNumber}
timestamp=${timestamp}

View File

@ -5,18 +5,42 @@
*/ */
package org.elasticsearch.shield.plugin; package org.elasticsearch.shield.plugin;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.node.internal.InternalNode;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.test.ShieldIntegrationTest; import org.elasticsearch.shield.test.ShieldIntegrationTest;
import org.elasticsearch.test.junit.annotations.TestLogging;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.junit.Test; import org.junit.Test;
import static org.hamcrest.Matchers.hasSize; import java.io.IOException;
import static org.hamcrest.Matchers.is;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.elasticsearch.rest.RestStatus.OK;
import static org.elasticsearch.rest.RestStatus.UNAUTHORIZED;
import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.hamcrest.Matchers.*;
public class ShieldPluginTests extends ShieldIntegrationTest { public class ShieldPluginTests extends ShieldIntegrationTest {
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.put(InternalNode.HTTP_ENABLED, true)
.put("shield.http.ssl", false)
.put("shield.audit.enabled", false)
.build();
}
@Test @Test
public void testThatPluginIsLoaded() { public void testThatPluginIsLoaded() throws IOException {
logger.info("--> Getting nodes info"); logger.info("--> Getting nodes info");
NodesInfoResponse nodeInfos = internalCluster().transportClient().admin().cluster().prepareNodesInfo().get(); NodesInfoResponse nodeInfos = internalCluster().transportClient().admin().cluster().prepareNodesInfo().get();
logger.info("--> Checking nodes info that shield plugin is loaded"); logger.info("--> Checking nodes info that shield plugin is loaded");
@ -24,6 +48,17 @@ public class ShieldPluginTests extends ShieldIntegrationTest {
assertThat(nodeInfo.getPlugins().getInfos(), hasSize(1)); assertThat(nodeInfo.getPlugins().getInfos(), hasSize(1));
assertThat(nodeInfo.getPlugins().getInfos().get(0).getName(), is(ShieldPlugin.NAME)); assertThat(nodeInfo.getPlugins().getInfos().get(0).getName(), is(ShieldPlugin.NAME));
} }
}
HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class);
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
logger.info("Executing unauthorized request to /_shield infos");
HttpResponse response = new HttpRequestBuilder(httpClient).httpTransport(httpServerTransport).method("GET").path("/_shield").execute();
assertThat(response.getStatusCode(), is(UNAUTHORIZED.getStatus()));
logger.info("Executing authorized request to /_shield infos");
response = new HttpRequestBuilder(httpClient).httpTransport(httpServerTransport).method("GET").path("/_shield").addHeader("Authorization", basicAuthHeaderValue(DEFAULT_USER_NAME, new SecuredString(DEFAULT_PASSWORD.toCharArray()))).execute();
assertThat(response.getStatusCode(), is(OK.getStatus()));
assertThat(response.getBody(), allOf(containsString("build_hash"), containsString("build_timestamp"), containsString("build_version")));
}
}
} }