GUACAMOLE-990: Enabled/disable auth failure tracking via implementations of a common interface.

This commit is contained in:
Michael Jumper
2022-08-22 15:08:42 -07:00
parent a9ed4c2982
commit 584db45a4f
6 changed files with 172 additions and 71 deletions

View File

@@ -19,6 +19,7 @@
package org.apache.guacamole.auth.ban;
import org.apache.guacamole.auth.ban.status.AuthenticationFailureTracker;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
import org.apache.guacamole.net.event.AuthenticationFailureEvent;

View File

@@ -19,8 +19,11 @@
package org.apache.guacamole.auth.ban;
import org.apache.guacamole.auth.ban.status.InMemoryAuthenticationFailureTracker;
import org.apache.guacamole.auth.ban.status.AuthenticationFailureTracker;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.auth.ban.status.NullAuthenticationFailureTracker;
import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.net.auth.AbstractAuthenticationProvider;
@@ -29,6 +32,8 @@ import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.properties.IntegerGuacamoleProperty;
import org.apache.guacamole.properties.LongGuacamoleProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* AuthenticationProvider implementation that blocks further authentication
@@ -37,6 +42,11 @@ import org.apache.guacamole.properties.LongGuacamoleProperty;
*/
public class BanningAuthenticationProvider extends AbstractAuthenticationProvider {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(BanningAuthenticationProvider.class);
/**
* The maximum number of failed authentication attempts allowed before an
* address is temporarily banned.
@@ -126,7 +136,29 @@ public class BanningAuthenticationProvider extends AbstractAuthenticationProvide
+ "\"" + MAX_ADDRESSES.getName() + "\" property, must be "
+ "greater than zero.");
tracker = new AuthenticationFailureTracker(maxAttempts, banDuration, maxAddresses);
// Configure auth failure tracking behavior and inform administrator of
// ultimate result
if (maxAttempts <= 0) {
this.tracker = new NullAuthenticationFailureTracker();
logger.info("Maximum failed authentication attempts has been set "
+ "to {}. Automatic banning of brute-force authentication "
+ "attempts will be disabled.", maxAttempts);
}
else if (banDuration <= 0) {
this.tracker = new NullAuthenticationFailureTracker();
logger.info("Ban duration for addresses that repeatedly fail "
+ "authentication has been set to {}. Automatic banning "
+ "of brute-force authentication attempts will be "
+ "disabled.", banDuration);
}
else {
this.tracker = new InMemoryAuthenticationFailureTracker(maxAttempts, banDuration, maxAddresses);
logger.info("Addresses will be automatically banned for {} "
+ "seconds after {} failed authentication attempts. Up "
+ "to {} unique addresses will be tracked/banned at any "
+ "given time.", banDuration, maxAttempts, maxAddresses);
}
BanningAuthenticationListener.setAuthenticationFailureTracker(tracker);
}

View File

@@ -17,7 +17,7 @@
* under the License.
*/
package org.apache.guacamole.auth.ban;
package org.apache.guacamole.auth.ban.status;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

View File

@@ -0,0 +1,78 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.guacamole.auth.ban.status;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.Credentials;
/**
* Tracks past authentication results, automatically blocking the IP addresses
* of machines that repeatedly fail to authenticate.
*/
public interface AuthenticationFailureTracker {
/**
* Reports that an authentication request has been received, but it is
* either not yet known whether the request has succeeded or failed. If the
* associated address is currently being blocked, an exception will be
* thrown.
*
* @param credentials
* The credentials associated with the authentication request.
*
* @throws GuacamoleException
* If the authentication request is being blocked due to brute force
* prevention rules.
*/
void notifyAuthenticationRequestReceived(Credentials credentials)
throws GuacamoleException;
/**
* Reports that an authentication request has been received and has
* succeeded. If the associated address is currently being blocked, an
* exception will be thrown.
*
* @param credentials
* The credentials associated with the successful authentication
* request.
*
* @throws GuacamoleException
* If the authentication request is being blocked due to brute force
* prevention rules.
*/
void notifyAuthenticationSuccess(Credentials credentials)
throws GuacamoleException;
/**
* Reports that an authentication request has been received and has
* failed. If the associated address is currently being blocked, an
* exception will be thrown.
*
* @param credentials
* The credentials associated with the failed authentication request.
*
* @throws GuacamoleException
* If the authentication request is being blocked due to brute force
* prevention rules.
*/
void notifyAuthenticationFailed(Credentials credentials)
throws GuacamoleException;
}

View File

@@ -17,7 +17,7 @@
* under the License.
*/
package org.apache.guacamole.auth.ban;
package org.apache.guacamole.auth.ban.status;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
@@ -30,15 +30,16 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides automated tracking and blocking of IP addresses that repeatedly
* fail authentication.
* AuthenticationFailureTracker implementation that tracks the failure status
* of each IP address in memory. The maximum amount of memory consumed is
* bounded by the configured maximum number of addresses tracked.
*/
public class AuthenticationFailureTracker {
public class InMemoryAuthenticationFailureTracker implements AuthenticationFailureTracker {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(AuthenticationFailureTracker.class);
private static final Logger logger = LoggerFactory.getLogger(InMemoryAuthenticationFailureTracker.class);
/**
* All authentication failures currently being tracked, stored by the
@@ -74,38 +75,17 @@ public class AuthenticationFailureTracker {
* The maximum number of unique IP addresses that should be tracked
* before discarding older tracked failures.
*/
public AuthenticationFailureTracker(int maxAttempts, int banDuration,
public InMemoryAuthenticationFailureTracker(int maxAttempts, int banDuration,
long maxAddresses) {
this.maxAttempts = maxAttempts;
this.banDuration = banDuration;
// Inform administrator of configured behavior
if (maxAttempts <= 0) {
logger.info("Maximum failed authentication attempts has been set "
+ "to {}. Automatic banning of brute-force authentication "
+ "attempts will be disabled.", maxAttempts);
}
else if (banDuration <= 0) {
logger.info("Ban duration for addresses that repeatedly fail "
+ "authentication has been set to {}. Automatic banning "
+ "of brute-force authentication attempts will be "
+ "disabled.", banDuration);
}
else {
logger.info("Addresses will be automatically banned for {} "
+ "seconds after {} failed authentication attempts.",
banDuration, maxAttempts);
}
// Limit maximum number of tracked addresses to configured upper bound
this.failures = Caffeine.newBuilder()
.maximumSize(maxAddresses)
.build();
logger.info("Up to {} unique addresses will be tracked/banned at any "
+ " given time.", maxAddresses);
}
/**
@@ -187,10 +167,6 @@ public class AuthenticationFailureTracker {
private void notifyAuthenticationStatus(Credentials credentials,
boolean failed) throws GuacamoleException {
// Do not track/ban if tracking or banning are disabled
if (maxAttempts <= 0 || banDuration <= 0)
return;
// Ignore requests that do not contain explicit parameters of any kind
if (isEmpty(credentials))
return;
@@ -234,54 +210,19 @@ public class AuthenticationFailureTracker {
}
/**
* Reports that an authentication request has been received, but it is
* either not yet known whether the request has succeeded or failed. If the
* associated address is currently being blocked, an exception will be
* thrown.
*
* @param credentials
* The credentials associated with the authentication request.
*
* @throws GuacamoleException
* If the authentication request is being blocked due to brute force
* prevention rules.
*/
@Override
public void notifyAuthenticationRequestReceived(Credentials credentials)
throws GuacamoleException {
notifyAuthenticationStatus(credentials, false);
}
/**
* Reports that an authentication request has been received and has
* succeeded. If the associated address is currently being blocked, an
* exception will be thrown.
*
* @param credentials
* The credentials associated with the successful authentication
* request.
*
* @throws GuacamoleException
* If the authentication request is being blocked due to brute force
* prevention rules.
*/
@Override
public void notifyAuthenticationSuccess(Credentials credentials)
throws GuacamoleException {
notifyAuthenticationStatus(credentials, false);
}
/**
* Reports that an authentication request has been received and has
* failed. If the associated address is currently being blocked, an
* exception will be thrown.
*
* @param credentials
* The credentials associated with the failed authentication request.
*
* @throws GuacamoleException
* If the authentication request is being blocked due to brute force
* prevention rules.
*/
@Override
public void notifyAuthenticationFailed(Credentials credentials)
throws GuacamoleException {
notifyAuthenticationStatus(credentials, true);

View File

@@ -0,0 +1,49 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.guacamole.auth.ban.status;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.Credentials;
/**
* AuthenticationFailureTracker implementation that does nothing. All requests
* are ignored, regardless of status, and no tracking is performed.
*/
public class NullAuthenticationFailureTracker implements AuthenticationFailureTracker {
@Override
public void notifyAuthenticationRequestReceived(Credentials credentials)
throws GuacamoleException {
// Do nothing
}
@Override
public void notifyAuthenticationSuccess(Credentials credentials)
throws GuacamoleException {
// Do nothing
}
@Override
public void notifyAuthenticationFailed(Credentials credentials)
throws GuacamoleException {
// Do nothing
}
}