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:
parent
df3956fafe
commit
4903852f48
18
pom.xml
18
pom.xml
|
@ -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>
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
version=${project.version}
|
||||||
|
hash=${buildNumber}
|
||||||
|
timestamp=${timestamp}
|
|
@ -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")));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue