Security: Audit all HTTP requests

Adds a new audit event (authentication_success) which logs each request made to
the REST API along with the body of the request

Closes elastic/elasticsearch#912

Original commit: elastic/x-pack-elasticsearch@650b9d70c0
This commit is contained in:
Colin Goodheart-Smithe 2016-08-30 16:32:12 +01:00
parent c7aabce3df
commit 8b6d988cfb
12 changed files with 327 additions and 31 deletions

View File

@ -12,6 +12,7 @@ import java.util.Locale;
public enum AuditLevel {
ANONYMOUS_ACCESS_DENIED,
AUTHENTICATION_FAILED,
REALM_AUTHENTICATION_FAILED,
@ -21,6 +22,7 @@ public enum AuditLevel {
CONNECTION_GRANTED,
CONNECTION_DENIED,
SYSTEM_ACCESS_GRANTED,
AUTHENTICATION_SUCCESS,
RUN_AS_GRANTED,
RUN_AS_DENIED;
@ -59,6 +61,9 @@ public enum AuditLevel {
case "system_access_granted":
enumSet.add(SYSTEM_ACCESS_GRANTED);
break;
case "authentication_success":
enumSet.add(AUTHENTICATION_SUCCESS);
break;
case "run_as_granted":
enumSet.add(RUN_AS_GRANTED);
break;

View File

@ -20,6 +20,10 @@ public interface AuditTrail {
String name();
void authenticationSuccess(String realm, User user, RestRequest request);
void authenticationSuccess(String realm, User user, String action, TransportMessage message);
void anonymousAccessDenied(String action, TransportMessage message);
void anonymousAccessDenied(RestRequest request);

View File

@ -48,6 +48,24 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
return auditTrails;
}
@Override
public void authenticationSuccess(String realm, User user, RestRequest request) {
if (licenseState.isAuditingAllowed()) {
for (AuditTrail auditTrail : auditTrails) {
auditTrail.authenticationSuccess(realm, user, request);
}
}
}
@Override
public void authenticationSuccess(String realm, User user, String action, TransportMessage message) {
if (licenseState.isAuditingAllowed()) {
for (AuditTrail auditTrail : auditTrails) {
auditTrail.authenticationSuccess(realm, user, action, message);
}
}
}
@Override
public void anonymousAccessDenied(String action, TransportMessage message) {
if (licenseState.isAuditingAllowed()) {

View File

@ -99,6 +99,7 @@ import static org.elasticsearch.xpack.security.audit.AuditLevel.RUN_AS_DENIED;
import static org.elasticsearch.xpack.security.audit.AuditLevel.RUN_AS_GRANTED;
import static org.elasticsearch.xpack.security.audit.AuditLevel.SYSTEM_ACCESS_GRANTED;
import static org.elasticsearch.xpack.security.audit.AuditLevel.TAMPERED_REQUEST;
import static org.elasticsearch.xpack.security.audit.AuditLevel.AUTHENTICATION_SUCCESS;
import static org.elasticsearch.xpack.security.audit.AuditLevel.parse;
import static org.elasticsearch.xpack.security.audit.index.IndexNameResolver.resolve;
@ -135,7 +136,8 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
CONNECTION_GRANTED.toString(),
TAMPERED_REQUEST.toString(),
RUN_AS_DENIED.toString(),
RUN_AS_GRANTED.toString()
RUN_AS_GRANTED.toString(),
AUTHENTICATION_SUCCESS.toString()
);
private static final String FORBIDDEN_INDEX_SETTING = "index.mapper.dynamic";
@ -311,11 +313,34 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
}
}
@Override
public void authenticationSuccess(String realm, User user, RestRequest request) {
if (events.contains(AUTHENTICATION_SUCCESS)) {
try {
enqueue(message("authentication_success", realm, user, request), "authentication_success");
} catch (Exception e) {
logger.warn("failed to index audit event: [authentication_success]", e);
}
}
}
@Override
public void authenticationSuccess(String realm, User user, String action, TransportMessage message) {
if (events.contains(AUTHENTICATION_SUCCESS)) {
try {
enqueue(message("authentication_success", action, user, realm, null, message), "authentication_success");
} catch (Exception e) {
logger.warn("failed to index audit event: [authentication_success]", e);
}
}
}
@Override
public void anonymousAccessDenied(String action, TransportMessage message) {
if (events.contains(ANONYMOUS_ACCESS_DENIED)) {
try {
enqueue(message("anonymous_access_denied", action, null, null, indices(message), message), "anonymous_access_denied");
enqueue(message("anonymous_access_denied", action, (User) null, null, indices(message), message),
"anonymous_access_denied");
} catch (Exception e) {
logger.warn("failed to index audit event: [anonymous_access_denied]", e);
}
@ -337,7 +362,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
public void authenticationFailed(String action, TransportMessage message) {
if (events.contains(AUTHENTICATION_FAILED)) {
try {
enqueue(message("authentication_failed", action, null, null, indices(message), message), "authentication_failed");
enqueue(message("authentication_failed", action, (User) null, null, indices(message), message), "authentication_failed");
} catch (Exception e) {
logger.warn("failed to index audit event: [authentication_failed]", e);
}
@ -413,14 +438,14 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
if ((SystemUser.is(user) && SystemPrivilege.INSTANCE.predicate().test(action))) {
if (events.contains(SYSTEM_ACCESS_GRANTED)) {
try {
enqueue(message("access_granted", action, user, indices(message), message), "access_granted");
enqueue(message("access_granted", action, user, null, indices(message), message), "access_granted");
} catch (Exception e) {
logger.warn("failed to index audit event: [access_granted]", e);
}
}
} else if (events.contains(ACCESS_GRANTED) && XPackUser.is(user) == false) {
try {
enqueue(message("access_granted", action, user, indices(message), message), "access_granted");
enqueue(message("access_granted", action, user, null, indices(message), message), "access_granted");
} catch (Exception e) {
logger.warn("failed to index audit event: [access_granted]", e);
}
@ -431,7 +456,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
public void accessDenied(User user, String action, TransportMessage message) {
if (events.contains(ACCESS_DENIED) && XPackUser.is(user) == false) {
try {
enqueue(message("access_denied", action, user, indices(message), message), "access_denied");
enqueue(message("access_denied", action, user, null, indices(message), message), "access_denied");
} catch (Exception e) {
logger.warn("failed to index audit event: [access_denied]", e);
}
@ -453,7 +478,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
public void tamperedRequest(String action, TransportMessage message) {
if (events.contains(TAMPERED_REQUEST)) {
try {
enqueue(message("tampered_request", action, null, indices(message), message), "tampered_request");
enqueue(message("tampered_request", action, (User) null, null, indices(message), message), "tampered_request");
} catch (Exception e) {
logger.warn("failed to index audit event: [tampered_request]", e);
}
@ -464,7 +489,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
public void tamperedRequest(User user, String action, TransportMessage request) {
if (events.contains(TAMPERED_REQUEST) && XPackUser.is(user) == false) {
try {
enqueue(message("tampered_request", action, user, indices(request), request), "tampered_request");
enqueue(message("tampered_request", action, user, null, indices(request), request), "tampered_request");
} catch (Exception e) {
logger.warn("failed to index audit event: [tampered_request]", e);
}
@ -497,7 +522,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
public void runAsGranted(User user, String action, TransportMessage message) {
if (events.contains(RUN_AS_GRANTED)) {
try {
enqueue(message("run_as_granted", action, user, null, message), "run_as_granted");
enqueue(message("run_as_granted", action, user, null, null, message), "run_as_granted");
} catch (Exception e) {
logger.warn("failed to index audit event: [run_as_granted]", e);
}
@ -508,7 +533,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
public void runAsDenied(User user, String action, TransportMessage message) {
if (events.contains(RUN_AS_DENIED)) {
try {
enqueue(message("run_as_denied", action, user, null, message), "run_as_denied");
enqueue(message("run_as_denied", action, user, null, null, message), "run_as_denied");
} catch (Exception e) {
logger.warn("failed to index audit event: [run_as_denied]", e);
}
@ -519,14 +544,14 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
public void runAsDenied(User user, RestRequest request) {
if (events.contains(RUN_AS_DENIED)) {
try {
enqueue(message("run_as_denied", user, request), "run_as_denied");
enqueue(message("run_as_denied", null, user, request), "run_as_denied");
} catch (Exception e) {
logger.warn("failed to index audit event: [run_as_denied]", e);
}
}
}
private Message message(String type, @Nullable String action, @Nullable User user,
private Message message(String type, @Nullable String action, @Nullable User user, @Nullable String realm,
@Nullable Set<String> indices, TransportMessage message) throws Exception {
Message msg = new Message().start();
@ -552,6 +577,9 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
if (indices != null) {
msg.builder.array(Field.INDICES, indices.toArray(Strings.EMPTY_ARRAY));
}
if (realm != null) {
msg.builder.field(Field.REALM, realm);
}
msg.builder.field(Field.REQUEST, message.getClass().getSimpleName());
return msg.end();
@ -583,7 +611,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
}
private Message message(String type, @Nullable String action, @Nullable AuthenticationToken token,
@Nullable String realm, @Nullable Set<String> indices, RestRequest request) throws Exception {
@Nullable String realm, @Nullable Set<String> indices, RestRequest request) throws Exception {
Message msg = new Message().start();
common("rest", type, msg.builder);
@ -614,16 +642,25 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
msg.builder.field(Field.ORIGIN_ADDRESS, address);
}
msg.builder.field(Field.URI, request.uri());
return msg.end();
}
private Message message(String type, User user, RestRequest request) throws Exception {
private Message message(String type, String realm, User user, RestRequest request) throws Exception {
Message msg = new Message().start();
common("rest", type, msg.builder);
msg.builder.field(Field.PRINCIPAL, user.principal());
if (user != null) {
if (user.runAs() != null) {
msg.builder.field(Field.PRINCIPAL, user.runAs().principal());
msg.builder.field(Field.RUN_BY_PRINCIPAL, user.principal());
} else {
msg.builder.field(Field.PRINCIPAL, user.principal());
}
}
if (realm != null) {
msg.builder.field(Field.REALM, realm);
}
if (includeRequestBody) {
msg.builder.field(Field.REQUEST_BODY, restRequestContent(request));
}

View File

@ -53,6 +53,7 @@ import static org.elasticsearch.xpack.security.audit.AuditLevel.RUN_AS_DENIED;
import static org.elasticsearch.xpack.security.audit.AuditLevel.RUN_AS_GRANTED;
import static org.elasticsearch.xpack.security.audit.AuditLevel.SYSTEM_ACCESS_GRANTED;
import static org.elasticsearch.xpack.security.audit.AuditLevel.TAMPERED_REQUEST;
import static org.elasticsearch.xpack.security.audit.AuditLevel.AUTHENTICATION_SUCCESS;
import static org.elasticsearch.xpack.security.audit.AuditLevel.parse;
import static org.elasticsearch.xpack.security.audit.AuditUtil.indices;
import static org.elasticsearch.xpack.security.audit.AuditUtil.restRequestContent;
@ -119,6 +120,28 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail {
return prefix;
}
@Override
public void authenticationSuccess(String realm, User user, RestRequest request) {
if (events.contains(AUTHENTICATION_SUCCESS)) {
if (includeRequestBody) {
logger.info("{}[rest] [authentication_success]\t{}, realm=[{}], uri=[{}], params=[{}], request_body=[{}]", getPrefix(),
principal(user), realm, request.uri(), request.params(), restRequestContent(request));
} else {
logger.info("{}[rest] [authentication_success]\t{}, realm=[{}], uri=[{}], params=[{}]", getPrefix(), principal(user), realm,
request.uri(), request.params());
}
}
}
@Override
public void authenticationSuccess(String realm, User user, String action, TransportMessage message) {
if (events.contains(AUTHENTICATION_SUCCESS)) {
logger.info("{}[transport] [authentication_success]\t{}, {}, realm=[{}], action=[{}], request=[{}]", getPrefix(),
originAttributes(message, clusterService.localNode(), threadContext), principal(user), realm, action,
message.getClass().getSimpleName());
}
}
@Override
public void anonymousAccessDenied(String action, TransportMessage message) {
if (events.contains(ANONYMOUS_ACCESS_DENIED)) {

View File

@ -153,7 +153,9 @@ public class AuthenticationService extends AbstractComponent {
AuthenticationToken token = extractToken();
if (token == null) {
return handleNullToken();
Authentication authentication = handleNullToken();
request.authenticationSuccess(authentication.getAuthenticatedBy().getName(), authentication.getUser());
return authentication;
}
User user = authenticateToken(token);
@ -165,6 +167,7 @@ public class AuthenticationService extends AbstractComponent {
final Authentication authentication = new Authentication(user, authenticatedBy, lookedupBy);
authentication.writeToContext(threadContext, cryptoService, signUserHeader);
request.authenticationSuccess(authentication.getAuthenticatedBy().getName(), user);
return authentication;
}
@ -322,6 +325,8 @@ public class AuthenticationService extends AbstractComponent {
abstract ElasticsearchSecurityException anonymousAccessDenied();
abstract ElasticsearchSecurityException runAsDenied(User user, AuthenticationToken token);
abstract void authenticationSuccess(String realm, User user);
}
class Transport extends AuditableRequest {
@ -334,6 +339,11 @@ public class AuthenticationService extends AbstractComponent {
this.message = message;
}
@Override
void authenticationSuccess(String realm, User user) {
auditTrail.authenticationSuccess(realm, user, action, message);
}
@Override
void realmAuthenticationFailed(AuthenticationToken token, String realm) {
auditTrail.authenticationFailed(realm, token, action, message);
@ -373,6 +383,7 @@ public class AuthenticationService extends AbstractComponent {
return failureHandler.failedAuthentication(message, token, action, threadContext);
}
@Override
public String toString() {
return "transport request action [" + action + "]";
}
@ -386,6 +397,11 @@ public class AuthenticationService extends AbstractComponent {
this.request = request;
}
@Override
void authenticationSuccess(String realm, User user) {
auditTrail.authenticationSuccess(realm, user, request);
}
@Override
void realmAuthenticationFailed(AuthenticationToken token, String realm) {
auditTrail.authenticationFailed(realm, token, request);
@ -425,6 +441,7 @@ public class AuthenticationService extends AbstractComponent {
return failureHandler.failedAuthentication(request, token, threadContext);
}
@Override
public String toString() {
return "rest request uri [" + request.uri() + "]";
}

View File

@ -47,7 +47,7 @@ public class SecurityRestFilter extends RestFilter {
@Inject
public SecurityRestFilter(AuthenticationService service, RestController controller, Settings settings,
ThreadPool threadPool, XPackLicenseState licenseState, SSLService sslService) {
ThreadPool threadPool, XPackLicenseState licenseState, SSLService sslService) {
this.service = service;
this.licenseState = licenseState;
this.threadContext = threadPool.getThreadContext();
@ -72,7 +72,7 @@ public class SecurityRestFilter extends RestFilter {
if (extractClientCertificate) {
putClientCertificateInContext(request, threadContext, logger);
}
service.authenticate(request);
service.authenticate(request).getUser();
}
RemoteHostHeader.process(request, threadContext);

View File

@ -192,4 +192,32 @@ public class AuditTrailServiceTests extends ESTestCase {
verifyZeroInteractions(auditTrails.toArray((Object[]) new AuditTrail[auditTrails.size()]));
}
}
public void testAuthenticationSuccessRest() throws Exception {
User user = new User("_username", "r1");
String realm = "_realm";
service.authenticationSuccess(realm, user, restRequest);
verify(licenseState).isAuditingAllowed();
if (isAuditingAllowed) {
for (AuditTrail auditTrail : auditTrails) {
verify(auditTrail).authenticationSuccess(realm, user, restRequest);
}
} else {
verifyZeroInteractions(auditTrails.toArray((Object[]) new AuditTrail[auditTrails.size()]));
}
}
public void testAuthenticationSuccessTransport() throws Exception {
User user = new User("_username", "r1");
String realm = "_realm";
service.authenticationSuccess(realm, user, "_action", message);
verify(licenseState).isAuditingAllowed();
if (isAuditingAllowed) {
for (AuditTrail auditTrail : auditTrails) {
verify(auditTrail).authenticationSuccess(realm, user, "_action", message);
}
} else {
verifyZeroInteractions(auditTrails.toArray((Object[]) new AuditTrail[auditTrails.size()]));
}
}
}

View File

@ -104,7 +104,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase {
auditTrail.anonymousAccessDenied(restRequest);
assertThat(messageEnqueued.get(), is(false));
assertThat(clientCalled.get(), is(false));
verifyZeroInteractions(restRequest);
}
@ -122,7 +122,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase {
auditTrail.authenticationFailed("_action", message);
assertThat(messageEnqueued.get(), is(false));
assertThat(clientCalled.get(), is(false));
verifyZeroInteractions(token, message);
}
@ -176,7 +176,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase {
auditTrail.accessGranted(user, randomAsciiOfLengthBetween(6, 40), message);
assertThat(messageEnqueued.get(), is(false));
assertThat(clientCalled.get(), is(false));
verifyZeroInteractions(message, user);
}
@ -187,7 +187,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase {
auditTrail.accessGranted(user, "internal:foo", message);
assertThat(messageEnqueued.get(), is(false));
assertThat(clientCalled.get(), is(false));
verifyZeroInteractions(message);
}
@ -198,7 +198,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase {
auditTrail.accessDenied(user, randomAsciiOfLengthBetween(6, 40), message);
assertThat(messageEnqueued.get(), is(false));
assertThat(clientCalled.get(), is(false));
verifyZeroInteractions(message, user);
}
@ -216,7 +216,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase {
auditTrail.tamperedRequest(randomAsciiOfLengthBetween(6, 40), message);
assertThat(messageEnqueued.get(), is(false));
assertThat(clientCalled.get(), is(false));
verifyZeroInteractions(message, user);
}
@ -228,7 +228,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase {
auditTrail.connectionGranted(address, randomAsciiOfLengthBetween(1, 12), rule);
assertThat(messageEnqueued.get(), is(false));
assertThat(clientCalled.get(), is(false));
verifyZeroInteractions(address, rule);
}
@ -240,7 +240,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase {
auditTrail.connectionDenied(address, randomAsciiOfLengthBetween(1, 12), rule);
assertThat(messageEnqueued.get(), is(false));
assertThat(clientCalled.get(), is(false));
verifyZeroInteractions(address, rule);
}
@ -252,7 +252,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase {
auditTrail.runAsGranted(user, randomAsciiOfLengthBetween(6, 40), message);
assertThat(messageEnqueued.get(), is(false));
assertThat(clientCalled.get(), is(false));
verifyZeroInteractions(message, user);
}
@ -264,7 +264,32 @@ public class IndexAuditTrailMutedTests extends ESTestCase {
auditTrail.runAsDenied(user, randomAsciiOfLengthBetween(6, 40), message);
assertThat(messageEnqueued.get(), is(false));
assertThat(clientCalled.get(), is(false));
verifyZeroInteractions(message, user);
}
public void testAuthenticationSuccessRest() {
createAuditTrail(new String[] { "authentication_success" });
RestRequest restRequest = mock(RestRequest.class);
User user = mock(User.class);
String realm = "_realm";
auditTrail.authenticationSuccess(realm, user, restRequest);
assertThat(messageEnqueued.get(), is(false));
assertThat(clientCalled.get(), is(false));
verifyZeroInteractions(restRequest);
}
public void testAuthenticationSuccessTransport() {
createAuditTrail(new String[] { "authentication_success" });
TransportMessage message = mock(TransportMessage.class);
User user = mock(User.class);
String realm = "_realm";
auditTrail.authenticationSuccess(realm, user, randomAsciiOfLengthBetween(6, 40), message);
assertThat(messageEnqueued.get(), is(false));
assertThat(clientCalled.get(), is(false));
verifyZeroInteractions(message, user);
}

View File

@ -53,7 +53,6 @@ import org.elasticsearch.xpack.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule;
import org.elasticsearch.xpack.security.transport.netty3.SecurityNetty3Transport;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.joda.time.DateTime;
@ -628,6 +627,61 @@ public class IndexAuditTrailTests extends SecurityIntegTestCase {
assertEquals(sourceMap.get("request"), message.getClass().getSimpleName());
}
public void testAuthenticationSuccessRest() throws Exception {
initialize();
RestRequest request = mockRestRequest();
final boolean runAs = randomBoolean();
User user;
if (runAs) {
user = new User("_username", new String[] { "r1" }, new User("running as", new String[] { "r2" }));
} else {
user = new User("_username", new String[] { "r1" });
}
String realm = "_realm";
auditor.authenticationSuccess(realm, user, request);
SearchHit hit = getIndexedAuditMessage(enqueuedMessage.get());
assertAuditMessage(hit, "rest", "authentication_success");
Map<String, Object> sourceMap = hit.sourceAsMap();
assertThat("_uri", equalTo(sourceMap.get("uri")));
assertRequestBody(sourceMap);
if (runAs) {
assertThat(sourceMap.get("principal"), is("running as"));
assertThat(sourceMap.get("run_by_principal"), is("_username"));
} else {
assertEquals("_username", sourceMap.get("principal"));
}
assertEquals("_realm", sourceMap.get("realm"));
}
public void testAuthenticationSuccessTransport() throws Exception {
initialize();
TransportMessage message = randomFrom(new RemoteHostMockMessage(), new LocalHostMockMessage(), new MockIndicesTransportMessage());
final boolean runAs = randomBoolean();
User user;
if (runAs) {
user = new User("_username", new String[] { "r1" }, new User("running as", new String[] { "r2" }));
} else {
user = new User("_username", new String[] { "r1" });
}
String realm = "_realm";
auditor.authenticationSuccess(realm, user, "_action", message);
SearchHit hit = getIndexedAuditMessage(enqueuedMessage.get());
Map<String, Object> sourceMap = hit.sourceAsMap();
assertAuditMessage(hit, "transport", "authentication_success");
assertEquals("transport", sourceMap.get("origin_type"));
if (runAs) {
assertThat(sourceMap.get("principal"), is("running as"));
assertThat(sourceMap.get("run_by_principal"), is("_username"));
} else {
assertEquals("_username", sourceMap.get("principal"));
}
assertEquals("_action", sourceMap.get("action"));
assertEquals("_realm", sourceMap.get("realm"));
assertEquals(sourceMap.get("request"), message.getClass().getSimpleName());
}
private void assertAuditMessage(SearchHit hit, String layer, String type) {
Map<String, Object> sourceMap = hit.sourceAsMap();
assertThat(sourceMap.get("@timestamp"), notNullValue());

View File

@ -35,8 +35,10 @@ import org.junit.Before;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
@ -601,6 +603,82 @@ public class LoggingAuditTrailTests extends ESTestCase {
}
}
public void testAuthenticationSuccessRest() throws Exception {
RestRequest request = mock(RestRequest.class);
InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1");
when(request.getRemoteAddress()).thenReturn(new InetSocketAddress(address, 9200));
when(request.uri()).thenReturn("_uri");
Map<String, String> params = new HashMap<>();
params.put("foo", "bar");
when(request.params()).thenReturn(params);
String expectedMessage = prepareRestContent(request);
boolean runAs = randomBoolean();
User user;
if (runAs) {
user = new User("_username", new String[] { "r1" }, new User("running as", new String[] { "r2" }));
} else {
user = new User("_username", new String[] { "r1" });
}
String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]";
String realm = "_realm";
Settings settings = Settings.builder().put(this.settings)
.put("xpack.security.audit.logfile.events.include", "authentication_success")
.build();
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
auditTrail.authenticationSuccess(realm, user, request);
if (includeRequestBody) {
assertMsg(logger, Level.INFO,
prefix + "[rest] [authentication_success]\t" + userInfo + ", realm=[_realm], uri=[_uri], params=[" + params
+ "], request_body=[" + expectedMessage + "]");
} else {
assertMsg(logger, Level.INFO,
prefix + "[rest] [authentication_success]\t" + userInfo + ", realm=[_realm], uri=[_uri], params=[" + params + "]");
}
// test disabled
CapturingLogger.output(logger.getName(), Level.INFO).clear();
settings = Settings.builder().put(this.settings).put("xpack.security.audit.logfile.events.exclude", "authentication_success")
.build();
auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
auditTrail.authenticationSuccess(realm, user, request);
assertEmptyLog(logger);
}
public void testAuthenticationSuccessTransport() throws Exception {
Settings settings = Settings.builder().put(this.settings)
.put("xpack.security.audit.logfile.events.include", "authentication_success").build();
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext);
String origins = LoggingAuditTrail.originAttributes(message, localNode, threadContext);
boolean runAs = randomBoolean();
User user;
if (runAs) {
user = new User("_username", new String[] { "r1" }, new User("running as", new String[] { "r2" }));
} else {
user = new User("_username", new String[] { "r1" });
}
String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]";
String realm = "_realm";
auditTrail.authenticationSuccess(realm, user, "_action", message);
if (message instanceof IndicesRequest) {
assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_success]\t" + origins + ", " + userInfo
+ ", realm=[_realm], action=[_action], request=[MockIndicesRequest]");
} else {
assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_success]\t" + origins + ", " + userInfo
+ ", realm=[_realm], action=[_action], request=[MockMessage]");
}
// test disabled
CapturingLogger.output(logger.getName(), Level.INFO).clear();
settings = Settings.builder().put(this.settings).put("xpack.security.audit.logfile.events.exclude", "authentication_success").build();
auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
auditTrail.authenticationSuccess(realm, user, "_action", message);
assertEmptyLog(logger);
}
private void assertMsg(Logger logger, Level level, String message) {
List<String> output = CapturingLogger.output(logger.getName(), level);
assertThat(output.size(), is(1));

View File

@ -166,7 +166,8 @@ public class AuthenticationServiceTests extends ESTestCase {
Authentication result = service.authenticate("_action", message, null);
assertThat(result, notNullValue());
assertThat(result.getUser(), is(user));
verifyZeroInteractions(auditTrail);
verify(auditTrail).authenticationSuccess(secondRealm.name(), user, "_action", message);
verifyNoMoreInteractions(auditTrail);
verify(firstRealm, never()).authenticate(token);
assertThreadContextContainsAuthentication(result);
}
@ -300,6 +301,8 @@ public class AuthenticationServiceTests extends ESTestCase {
assertThat(result, notNullValue());
assertThat(result.getUser(), sameInstance(user));
assertThreadContextContainsAuthentication(result);
verify(auditTrail).authenticationSuccess(firstRealm.name(), user, "_action", message);
verifyNoMoreInteractions(auditTrail);
}
public void testAuthenticateRestSuccess() throws Exception {
@ -311,6 +314,8 @@ public class AuthenticationServiceTests extends ESTestCase {
assertThat(result, notNullValue());
assertThat(result.getUser(), sameInstance(user1));
assertThreadContextContainsAuthentication(result);
verify(auditTrail).authenticationSuccess(firstRealm.name(), user1, restRequest);
verifyNoMoreInteractions(auditTrail);
}
public void testAutheticateTransportContextAndHeader() throws Exception {
@ -472,6 +477,8 @@ public class AuthenticationServiceTests extends ESTestCase {
assertThat(result, notNullValue());
assertThat(result.getUser(), sameInstance((Object) anonymousUser));
assertThreadContextContainsAuthentication(result);
verify(auditTrail).authenticationSuccess("__anonymous", new AnonymousUser(settings), request);
verifyNoMoreInteractions(auditTrail);
}
public void testAnonymousUserTransportNoDefaultUser() throws Exception {