Skip to content

Commit

Permalink
Log Failed Authentication Attempts if option is set (#2041)
Browse files Browse the repository at this point in the history
Log Failed Authentication Attempts if option is set
  • Loading branch information
pgermishuys authored and hayley-jean committed Nov 1, 2019
1 parent 56a072f commit ff75db1
Show file tree
Hide file tree
Showing 17 changed files with 71 additions and 26 deletions.
Expand Up @@ -10,7 +10,7 @@ internal class EmbeddedAuthenticationRequest : AuthenticationRequest {

internal EmbeddedAuthenticationRequest(
string name, string suppliedPassword, Action<Exception> setException, Action<IPrincipal> onAuthenticated)
: base(name, suppliedPassword) {
: base("embedded", name, suppliedPassword) {
_onAuthenticated = onAuthenticated;
_setException = setException;
}
Expand Down
4 changes: 4 additions & 0 deletions src/EventStore.ClusterNode/ClusterNodeOptions.cs
Expand Up @@ -291,6 +291,9 @@ public class ClusterNodeOptions : IOptions {

[ArgDescription(Opts.LogHttpRequestsDescr, Opts.AppGroup)]
public bool LogHttpRequests { get; set; }

[ArgDescription(Opts.LogFailedAuthenticationAttemptsDescr, Opts.AppGroup)]
public bool LogFailedAuthenticationAttempts { get; set; }

[ArgDescription(Opts.AlwaysKeepScavengedDescr, Opts.DbGroup)]
public bool AlwaysKeepScavenged { get; set; }
Expand Down Expand Up @@ -418,6 +421,7 @@ public class ClusterNodeOptions : IOptions {
StartStandardProjections = Opts.StartStandardProjectionsDefault;
DisableHTTPCaching = Opts.DisableHttpCachingDefault;
LogHttpRequests = Opts.LogHttpRequestsDefault;
LogFailedAuthenticationAttempts = Opts.LogFailedAuthenticationAttemptsDefault;

Unbuffered = Opts.UnbufferedDefault;
WriteThrough = Opts.WriteThroughDefault;
Expand Down
2 changes: 2 additions & 0 deletions src/EventStore.ClusterNode/Program.cs
Expand Up @@ -265,6 +265,8 @@ public class Program : ProgramBase<ClusterNodeOptions> {
builder.DisableScavengeMerging();
if (options.LogHttpRequests)
builder.EnableLoggingOfHttpRequests();
if (options.LogFailedAuthenticationAttempts)
builder.EnableLoggingOfFailedAuthenticationAttempts();
if (options.EnableHistograms)
builder.EnableHistograms();
if (options.UnsafeIgnoreHardDelete)
Expand Down
Expand Up @@ -21,7 +21,7 @@ public class with_internal_authentication_provider : TestFixtureWithExistingEven

PasswordHashAlgorithm passwordHashAlgorithm = new StubPasswordHashAlgorithm();
_internalAuthenticationProvider =
new InternalAuthenticationProvider(_ioDispatcher, passwordHashAlgorithm, 1000);
new InternalAuthenticationProvider(_ioDispatcher, passwordHashAlgorithm, 1000, false);
_bus.Subscribe(_internalAuthenticationProvider);
}
}
Expand All @@ -34,7 +34,7 @@ class TestAuthenticationRequest : AuthenticationRequest {

public TestAuthenticationRequest(string name, string suppliedPassword, Action unauthorized,
Action<IPrincipal> authenticated, Action error, Action notReady)
: base(name, suppliedPassword) {
: base("test", name, suppliedPassword) {
_unauthorized = unauthorized;
_authenticated = authenticated;
_error = error;
Expand Down
Expand Up @@ -6,7 +6,7 @@
namespace EventStore.Core.Tests.Common.VNodeBuilderTests {
public class TestAuthenticationProviderFactory : IAuthenticationProviderFactory {
public IAuthenticationProvider BuildAuthenticationProvider(IPublisher mainQueue, ISubscriber mainBus,
IPublisher workersQueue, InMemoryBus[] workerBusses) {
IPublisher workersQueue, InMemoryBus[] workerBuses, bool logFailedAuthenticationAttempts) {
return new TestAuthenticationProvider();
}

Expand Down
Expand Up @@ -32,7 +32,7 @@ public class TcpClientDispatcherTests {
Guid.NewGuid().ToString(), TcpServiceType.External, new ClientTcpDispatcher(),
InMemoryBus.CreateTest(), dummyConnection, InMemoryBus.CreateTest(), new InternalAuthenticationProvider(
new Core.Helpers.IODispatcher(InMemoryBus.CreateTest(), new NoopEnvelope()),
new StubPasswordHashAlgorithm(), 1),
new StubPasswordHashAlgorithm(), 1, false),
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { },
Opts.ConnectionPendingSendBytesThresholdDefault, Opts.ConnectionQueueSizeThresholdDefault);
}
Expand Down
Expand Up @@ -34,7 +34,7 @@ public class TcpConnectionManagerTests {
InMemoryBus.CreateTest(), dummyConnection, InMemoryBus.CreateTest(),
new InternalAuthenticationProvider(
new Core.Helpers.IODispatcher(InMemoryBus.CreateTest(), new NoopEnvelope()),
new StubPasswordHashAlgorithm(), 1),
new StubPasswordHashAlgorithm(), 1, false),
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { },
_connectionPendingSendBytesThreshold, _connectionQueueSizeThreshold);

Expand Down Expand Up @@ -74,7 +74,7 @@ public class TcpConnectionManagerTests {
Guid.NewGuid().ToString(), TcpServiceType.Internal, new ClientTcpDispatcher(),
publisher, dummyConnection, publisher,
new InternalAuthenticationProvider(new Core.Helpers.IODispatcher(publisher, new NoopEnvelope()),
new StubPasswordHashAlgorithm(), 1),
new StubPasswordHashAlgorithm(), 1, false),
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { },
_connectionPendingSendBytesThreshold, _connectionQueueSizeThreshold);

Expand Down Expand Up @@ -108,7 +108,7 @@ public void
Guid.NewGuid().ToString(), TcpServiceType.External, new ClientTcpDispatcher(),
InMemoryBus.CreateTest(), dummyConnection, InMemoryBus.CreateTest(),
new InternalAuthenticationProvider(
new Core.Helpers.IODispatcher(InMemoryBus.CreateTest(), new NoopEnvelope()), null, 1),
new Core.Helpers.IODispatcher(InMemoryBus.CreateTest(), new NoopEnvelope()), null, 1, false),
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { mre.Set(); },
_connectionPendingSendBytesThreshold, _connectionQueueSizeThreshold);

Expand Down Expand Up @@ -136,7 +136,7 @@ public void
Guid.NewGuid().ToString(), TcpServiceType.External, new ClientTcpDispatcher(),
InMemoryBus.CreateTest(), dummyConnection, InMemoryBus.CreateTest(),
new InternalAuthenticationProvider(
new Core.Helpers.IODispatcher(InMemoryBus.CreateTest(), new NoopEnvelope()), null, 1),
new Core.Helpers.IODispatcher(InMemoryBus.CreateTest(), new NoopEnvelope()), null, 1, false),
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { },
_connectionPendingSendBytesThreshold, _connectionQueueSizeThreshold);

Expand Down Expand Up @@ -168,7 +168,7 @@ public void
Guid.NewGuid().ToString(), TcpServiceType.External, new ClientTcpDispatcher(),
InMemoryBus.CreateTest(), dummyConnection, InMemoryBus.CreateTest(),
new InternalAuthenticationProvider(
new Core.Helpers.IODispatcher(InMemoryBus.CreateTest(), new NoopEnvelope()), null, 1),
new Core.Helpers.IODispatcher(InMemoryBus.CreateTest(), new NoopEnvelope()), null, 1, false),
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { mre.Set(); },
ESConsts.UnrestrictedPendingSendBytes, ESConsts.MaxConnectionQueueSize);

Expand Down Expand Up @@ -200,7 +200,7 @@ public void
Guid.NewGuid().ToString(), TcpServiceType.External, new ClientTcpDispatcher(),
InMemoryBus.CreateTest(), dummyConnection, InMemoryBus.CreateTest(),
new InternalAuthenticationProvider(
new Core.Helpers.IODispatcher(InMemoryBus.CreateTest(), new NoopEnvelope()), null, 1),
new Core.Helpers.IODispatcher(InMemoryBus.CreateTest(), new NoopEnvelope()), null, 1, false),
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { mre.Set(); },
ESConsts.UnrestrictedPendingSendBytes, ESConsts.MaxConnectionQueueSize);

Expand Down Expand Up @@ -232,7 +232,7 @@ public void
Guid.NewGuid().ToString(), TcpServiceType.External, new ClientTcpDispatcher(),
InMemoryBus.CreateTest(), dummyConnection, InMemoryBus.CreateTest(),
new InternalAuthenticationProvider(
new Core.Helpers.IODispatcher(InMemoryBus.CreateTest(), new NoopEnvelope()), null, 1),
new Core.Helpers.IODispatcher(InMemoryBus.CreateTest(), new NoopEnvelope()), null, 1, false),
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { mre.Set(); },
ESConsts.UnrestrictedPendingSendBytes, ESConsts.MaxConnectionQueueSize);

Expand Down
4 changes: 3 additions & 1 deletion src/EventStore.Core/Authentication/AuthenticationRequest.cs
Expand Up @@ -2,10 +2,12 @@

namespace EventStore.Core.Authentication {
public abstract class AuthenticationRequest {
public readonly string Id;
public readonly string Name;
public readonly string SuppliedPassword;

protected AuthenticationRequest(string name, string suppliedPassword) {
protected AuthenticationRequest(string id, string name, string suppliedPassword) {
Id = id;
Name = name;
SuppliedPassword = suppliedPassword;
}
Expand Down
Expand Up @@ -5,7 +5,7 @@
namespace EventStore.Core.Authentication {
public interface IAuthenticationProviderFactory {
IAuthenticationProvider BuildAuthenticationProvider(IPublisher mainQueue, ISubscriber mainBus,
IPublisher workersQueue, InMemoryBus[] workerBusses);
IPublisher workersQueue, InMemoryBus[] workerBuses, bool logFailedAuthenticationAttempts);

void RegisterHttpControllers(HttpService externalHttpService, HttpService internalHttpService,
HttpSendService httpSendService, IPublisher mainQueue, IPublisher networkSendQueue);
Expand Down
@@ -1,5 +1,6 @@
using System;
using System.Security.Principal;
using EventStore.Common.Log;
using EventStore.Common.Utils;
using EventStore.Core.Bus;
using EventStore.Core.Data;
Expand All @@ -11,15 +12,18 @@
namespace EventStore.Core.Authentication {
public class InternalAuthenticationProvider : IAuthenticationProvider,
IHandle<InternalAuthenticationProviderMessages.ResetPasswordCache> {
private static readonly ILogger Log = LogManager.GetLoggerFor<InternalAuthenticationProvider>();
private readonly IODispatcher _ioDispatcher;
private readonly PasswordHashAlgorithm _passwordHashAlgorithm;
private readonly bool _logFailedAuthenticationAttempts;
private readonly LRUCache<string, Tuple<string, IPrincipal>> _userPasswordsCache;

public InternalAuthenticationProvider(IODispatcher ioDispatcher, PasswordHashAlgorithm passwordHashAlgorithm,
int cacheSize) {
int cacheSize, bool logFailedAuthenticationAttempts) {
_ioDispatcher = ioDispatcher;
_passwordHashAlgorithm = passwordHashAlgorithm;
_userPasswordsCache = new LRUCache<string, Tuple<string, IPrincipal>>(cacheSize);
_logFailedAuthenticationAttempts = logFailedAuthenticationAttempts;
}

public void Authenticate(AuthenticationRequest authenticationRequest) {
Expand All @@ -39,11 +43,16 @@ public class InternalAuthenticationProvider : IAuthenticationProvider,
if (completed.Result == ReadStreamResult.StreamDeleted ||
completed.Result == ReadStreamResult.NoStream ||
completed.Result == ReadStreamResult.AccessDenied) {
if (_logFailedAuthenticationAttempts)
Log.Warn("Authentication Failed for {id}: {reason}", authenticationRequest.Id, "Invalid user.");
authenticationRequest.Unauthorized();
return;
}

if (completed.Result == ReadStreamResult.Error) {
if (_logFailedAuthenticationAttempts)
Log.Warn("Authentication Failed for {id}: {reason}", authenticationRequest.Id,
"The system is not ready.");
authenticationRequest.NotReady();
return;
}
Expand All @@ -54,17 +63,24 @@ public class InternalAuthenticationProvider : IAuthenticationProvider,
return;
}

if (userData.Disabled)
if (userData.Disabled) {
if (_logFailedAuthenticationAttempts)
Log.Warn("Authentication Failed for {id}: {reason}", authenticationRequest.Id,
"The account is disabled.");
authenticationRequest.Unauthorized();
else
} else {
AuthenticateWithPasswordHash(authenticationRequest, userData);
}
} catch {
authenticationRequest.Unauthorized();
}
}

private void AuthenticateWithPasswordHash(AuthenticationRequest authenticationRequest, UserData userData) {
if (!_passwordHashAlgorithm.Verify(authenticationRequest.SuppliedPassword, userData.Hash, userData.Salt)) {
if (_logFailedAuthenticationAttempts)
Log.Warn("Authentication Failed for {id}: {reason}", authenticationRequest.Id,
"Invalid credentials supplied.");
authenticationRequest.Unauthorized();
return;
}
Expand All @@ -90,6 +106,9 @@ public class InternalAuthenticationProvider : IAuthenticationProvider,
private void AuthenticateWithPassword(AuthenticationRequest authenticationRequest, string correctPassword,
IPrincipal principal) {
if (authenticationRequest.SuppliedPassword != correctPassword) {
if (_logFailedAuthenticationAttempts)
Log.Warn("Authentication Failed for {id}: {reason}", authenticationRequest.Id,
"Invalid credentials supplied.");
authenticationRequest.Unauthorized();
return;
}
Expand Down
Expand Up @@ -12,11 +12,11 @@
namespace EventStore.Core.Authentication {
public class InternalAuthenticationProviderFactory : IAuthenticationProviderFactory {
public IAuthenticationProvider BuildAuthenticationProvider(IPublisher mainQueue, ISubscriber mainBus,
IPublisher workersQueue, InMemoryBus[] workerBusses) {
IPublisher workersQueue, InMemoryBus[] workerBuses, bool logFailedAuthenticationAttempts) {
var passwordHashAlgorithm = new Rfc2898PasswordHashAlgorithm();
var dispatcher = new IODispatcher(mainQueue, new PublishEnvelope(workersQueue, crossThread: true));

foreach (var bus in workerBusses) {
foreach (var bus in workerBuses) {
bus.Subscribe(dispatcher.ForwardReader);
bus.Subscribe(dispatcher.BackwardReader);
bus.Subscribe(dispatcher.Writer);
Expand Down Expand Up @@ -48,7 +48,7 @@ public class InternalAuthenticationProviderFactory : IAuthenticationProviderFact
mainBus.Subscribe<SystemMessage.BecomeMaster>(userManagement);

var provider =
new InternalAuthenticationProvider(dispatcher, passwordHashAlgorithm, ESConsts.CachedPrincipalCount);
new InternalAuthenticationProvider(dispatcher, passwordHashAlgorithm, ESConsts.CachedPrincipalCount, logFailedAuthenticationAttempts);
var passwordChangeNotificationReader = new PasswordChangeNotificationReader(mainQueue, dispatcher);
mainBus.Subscribe<SystemMessage.SystemStart>(passwordChangeNotificationReader);
mainBus.Subscribe<SystemMessage.BecomeShutdown>(passwordChangeNotificationReader);
Expand Down
5 changes: 4 additions & 1 deletion src/EventStore.Core/Cluster/Settings/ClusterVNodeSettings.cs
Expand Up @@ -22,6 +22,7 @@ public class ClusterVNodeSettings {
public readonly bool StartStandardProjections;
public readonly bool DisableHTTPCaching;
public readonly bool LogHttpRequests;
public readonly bool LogFailedAuthenticationAttempts;

public readonly bool DiscoverViaDns;
public readonly string ClusterDns;
Expand Down Expand Up @@ -153,7 +154,8 @@ public class ClusterVNodeSettings {
bool faultOutOfOrderProjections = false,
bool structuredLog = false,
int maxAutoMergeIndexLevel = 1000,
bool disableFirstLevelHttpAuthorization = false) {
bool disableFirstLevelHttpAuthorization = false,
bool logFailedAuthenticationAttempts = false) {
Ensure.NotEmptyGuid(instanceId, "instanceId");
Ensure.NotNull(internalTcpEndPoint, "internalTcpEndPoint");
Ensure.NotNull(externalTcpEndPoint, "externalTcpEndPoint");
Expand Down Expand Up @@ -192,6 +194,7 @@ public class ClusterVNodeSettings {
StartStandardProjections = startStandardProjections;
DisableHTTPCaching = disableHTTPCaching;
LogHttpRequests = logHttpRequests;
LogFailedAuthenticationAttempts = logFailedAuthenticationAttempts;
AdditionalConsumerStrategies =
additionalConsumerStrategies ?? new IPersistentSubscriptionConsumerStrategyFactory[0];

Expand Down
2 changes: 1 addition & 1 deletion src/EventStore.Core/ClusterVNode.cs
Expand Up @@ -281,7 +281,7 @@ public class ClusterVNode :
// AUTHENTICATION INFRASTRUCTURE - delegate to plugins
_internalAuthenticationProvider =
vNodeSettings.AuthenticationProviderFactory.BuildAuthenticationProvider(_mainQueue, _mainBus,
_workersHandler, _workerBuses);
_workersHandler, _workerBuses, vNodeSettings.LogFailedAuthenticationAttempts);

Ensure.NotNull(_internalAuthenticationProvider, "authenticationProvider");

Expand Down
Expand Up @@ -34,7 +34,7 @@ private class HttpBasicAuthenticationRequest : AuthenticationRequest {
public HttpBasicAuthenticationRequest(
BasicHttpAuthenticationProvider basicHttpAuthenticationProvider, IncomingHttpRequestMessage message,
string name, string suppliedPassword)
: base(name, suppliedPassword) {
: base($"(HTTP) {message.Entity.Request?.RemoteEndPoint}", name, suppliedPassword) {
_basicHttpAuthenticationProvider = basicHttpAuthenticationProvider;
_message = message;
}
Expand Down
Expand Up @@ -455,7 +455,7 @@ private class TcpAuthRequest : AuthenticationRequest {
private readonly TcpPackage _package;

public TcpAuthRequest(TcpConnectionManager manager, TcpPackage package, string login, string password)
: base(login, password) {
: base($"(TCP) {manager.RemoteEndPoint}", login, password) {
_manager = manager;
_package = package;
}
Expand Down Expand Up @@ -485,7 +485,7 @@ private class TcpDefaultAuthRequest : AuthenticationRequest {

public TcpDefaultAuthRequest(TcpConnectionManager manager, Guid correlationId,
UserCredentials userCredentials)
: base(userCredentials.Login, userCredentials.Password) {
: base($"(TCP) {manager.RemoteEndPoint}", userCredentials.Login, userCredentials.Password) {
_manager = manager;
_correlationId = correlationId;
_userCredentials = userCredentials;
Expand Down
3 changes: 3 additions & 0 deletions src/EventStore.Core/Util/Opts.cs
Expand Up @@ -194,6 +194,9 @@ public static class Opts {

public const string LogHttpRequestsDescr = "Log Http Requests and Responses before processing them.";
public static readonly bool LogHttpRequestsDefault = false;

public const string LogFailedAuthenticationAttemptsDescr = "Log the failed authentication attempts.";
public static readonly bool LogFailedAuthenticationAttemptsDefault = false;

public const string SkipIndexScanOnReadsDescr =
"Skip Index Scan on Reads. This skips the index scan which was used to stop reading duplicates.";
Expand Down
14 changes: 13 additions & 1 deletion src/EventStore.Core/VNodeBuilder.cs
Expand Up @@ -76,6 +76,7 @@ public abstract class VNodeBuilder {

protected IAuthenticationProviderFactory _authenticationProviderFactory;
protected bool _disableFirstLevelHttpAuthorization;
protected bool _logFailedAuthenticationAttempts;
protected bool _disableScavengeMerging;
protected int _scavengeHistoryMaxAge;
protected bool _adminOnPublic;
Expand Down Expand Up @@ -213,6 +214,7 @@ public abstract class VNodeBuilder {
_startStandardProjections = Opts.StartStandardProjectionsDefault;
_disableHTTPCaching = Opts.DisableHttpCachingDefault;
_logHttpRequests = Opts.LogHttpRequestsDefault;
_logFailedAuthenticationAttempts = Opts.LogFailedAuthenticationAttemptsDefault;
_enableHistograms = Opts.LogHttpRequestsDefault;
_index = null;
_skipIndexVerify = Opts.SkipIndexVerifyDefault;
Expand Down Expand Up @@ -905,6 +907,15 @@ public abstract class VNodeBuilder {
_logHttpRequests = true;
return this;
}

/// <summary>
/// Enable logging of Failed Authentication Attempts
/// </summary>
/// <returns>A <see cref="VNodeBuilder"/> with the options set</returns>
public VNodeBuilder EnableLoggingOfFailedAuthenticationAttempts() {
_logFailedAuthenticationAttempts = true;
return this;
}

/// <summary>
/// Enable the tracking of various histograms in the backend, typically only used for debugging
Expand Down Expand Up @@ -1406,7 +1417,8 @@ public VNodeBuilder DisableFirstLevelHttpAuthorization()
_faultOutOfOrderProjections,
_structuredLog,
_maxAutoMergeIndexLevel,
_disableFirstLevelHttpAuthorization);
_disableFirstLevelHttpAuthorization,
_logFailedAuthenticationAttempts);

var infoController = new InfoController(options, _projectionType);

Expand Down

0 comments on commit ff75db1

Please sign in to comment.