Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add opaque_id to index audit logging #32260

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -882,6 +883,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