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:
parent
c7aabce3df
commit
8b6d988cfb
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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() + "]";
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue