Skip to content

Commit

Permalink
Add opaque_id to index audit logging (#32260)
Browse files Browse the repository at this point in the history
Logs opaque_id if it is available with all audit log messages using
index-based audit log.

Closes #31521
  • Loading branch information
imotov committed Jul 25, 2018
1 parent 765fcc3 commit bb10c8a
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 22 deletions.
3 changes: 3 additions & 0 deletions x-pack/plugin/core/src/main/resources/security_audit_log.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@
},
"rule": {
"type": "keyword"
},
"opaque_id": {
"type": "keyword"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.elasticsearch.node.Node;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportMessage;
import org.elasticsearch.xpack.core.XPackClientPlugin;
Expand Down Expand Up @@ -885,6 +886,12 @@ private XContentBuilder common(String layer, String type, XContentBuilder builde
builder.field(Field.NODE_HOST_ADDRESS, nodeHostAddress);
builder.field(Field.LAYER, layer);
builder.field(Field.TYPE, type);

String opaqueId = threadPool.getThreadContext().getHeader(Task.X_OPAQUE_ID);
if (opaqueId != null) {
builder.field("opaque_id", opaqueId);
}

return builder;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,19 @@
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.TestCluster;
import org.elasticsearch.xpack.core.XPackClientPlugin;
Expand Down Expand Up @@ -112,8 +116,53 @@ public void testIndexAuditTrailWorking() throws Exception {
UsernamePasswordToken.basicAuthHeaderValue(USER, new SecureString(PASS.toCharArray()))));
assertThat(response.getStatusLine().getStatusCode(), is(200));
final AtomicReference<ClusterState> lastClusterState = new AtomicReference<>();
final boolean found = awaitSecurityAuditIndex(lastClusterState, QueryBuilders.matchQuery("principal", USER));

assertTrue("Did not find security audit index. Current cluster state:\n" + lastClusterState.get().toString(), found);

SearchResponse searchResponse = client().prepareSearch(".security_audit_log*").setQuery(
QueryBuilders.matchQuery("principal", USER)).get();
assertThat(searchResponse.getHits().getHits().length, greaterThan(0));
assertThat(searchResponse.getHits().getAt(0).getSourceAsMap().get("principal"), is(USER));
}

public void testAuditTrailTemplateIsRecreatedAfterDelete() throws Exception {
// this is already "tested" by the test framework since we wipe the templates before and after,
// but lets be explicit about the behavior
awaitIndexTemplateCreation();

// delete the template
DeleteIndexTemplateResponse deleteResponse = client().admin().indices()
.prepareDeleteTemplate(IndexAuditTrail.INDEX_TEMPLATE_NAME).execute().actionGet();
assertThat(deleteResponse.isAcknowledged(), is(true));
awaitIndexTemplateCreation();
}

public void testOpaqueIdWorking() throws Exception {
Request request = new Request("GET", "/");
RequestOptions.Builder options = request.getOptions().toBuilder();
options.addHeader(Task.X_OPAQUE_ID, "foo");
options.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(USER, new SecureString(PASS.toCharArray())));
request.setOptions(options);
Response response = getRestClient().performRequest(request);
assertThat(response.getStatusLine().getStatusCode(), is(200));
final AtomicReference<ClusterState> lastClusterState = new AtomicReference<>();
final boolean found = awaitSecurityAuditIndex(lastClusterState, QueryBuilders.matchQuery("opaque_id", "foo"));

assertTrue("Did not find security audit index. Current cluster state:\n" + lastClusterState.get().toString(), found);

SearchResponse searchResponse = client().prepareSearch(".security_audit_log*").setQuery(
QueryBuilders.matchQuery("opaque_id", "foo")).get();
assertThat(searchResponse.getHits().getHits().length, greaterThan(0));

assertThat(searchResponse.getHits().getAt(0).getSourceAsMap().get("opaque_id"), is("foo"));
}

private boolean awaitSecurityAuditIndex(AtomicReference<ClusterState> lastClusterState,
QueryBuilder query) throws InterruptedException {
final AtomicBoolean indexExists = new AtomicBoolean(false);
final boolean found = awaitBusy(() -> {
return awaitBusy(() -> {
if (indexExists.get() == false) {
ClusterState state = client().admin().cluster().prepareState().get().getState();
lastClusterState.set(state);
Expand All @@ -138,28 +187,9 @@ public void testIndexAuditTrailWorking() throws Exception {
logger.info("refreshing audit indices");
client().admin().indices().prepareRefresh(".security_audit_log*").get();
logger.info("refreshed audit indices");
return client().prepareSearch(".security_audit_log*").setQuery(QueryBuilders.matchQuery("principal", USER))
.get().getHits().getTotalHits() > 0;
return client().prepareSearch(".security_audit_log*").setQuery(query)
.get().getHits().getTotalHits() > 0;
}, 60L, TimeUnit.SECONDS);

assertTrue("Did not find security audit index. Current cluster state:\n" + lastClusterState.get().toString(), found);

SearchResponse searchResponse = client().prepareSearch(".security_audit_log*").setQuery(
QueryBuilders.matchQuery("principal", USER)).get();
assertThat(searchResponse.getHits().getHits().length, greaterThan(0));
assertThat(searchResponse.getHits().getAt(0).getSourceAsMap().get("principal"), is(USER));
}

public void testAuditTrailTemplateIsRecreatedAfterDelete() throws Exception {
// this is already "tested" by the test framework since we wipe the templates before and after,
// but lets be explicit about the behavior
awaitIndexTemplateCreation();

// delete the template
DeleteIndexTemplateResponse deleteResponse = client().admin().indices()
.prepareDeleteTemplate(IndexAuditTrail.INDEX_TEMPLATE_NAME).execute().actionGet();
assertThat(deleteResponse.isAcknowledged(), is(true));
awaitIndexTemplateCreation();
}

private void awaitIndexTemplateCreation() throws InterruptedException {
Expand Down

0 comments on commit bb10c8a

Please sign in to comment.