mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
GUACAMOLE-990: Add extension for automatically blocking brute-force auth attempts.
This commit is contained in:
0
extensions/guacamole-auth-ban/.ratignore
Normal file
0
extensions/guacamole-auth-ban/.ratignore
Normal file
60
extensions/guacamole-auth-ban/pom.xml
Normal file
60
extensions/guacamole-auth-ban/pom.xml
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>guacamole-auth-ban</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.4.0</version>
|
||||
<name>guacamole-auth-ban</name>
|
||||
<url>http://guacamole.apache.org/</url>
|
||||
|
||||
<parent>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>extensions</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Java servlet API -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>2.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Guacamole Extension API -->
|
||||
<dependency>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>guacamole-ext</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
54
extensions/guacamole-auth-ban/src/main/assembly/dist.xml
Normal file
54
extensions/guacamole-auth-ban/src/main/assembly/dist.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<assembly
|
||||
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0
|
||||
http://maven.apache.org/xsd/assembly-1.1.0.xsd">
|
||||
|
||||
<id>dist</id>
|
||||
<baseDirectory>${project.artifactId}-${project.version}</baseDirectory>
|
||||
|
||||
<!-- Output tar.gz -->
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
|
||||
<!-- Include licenses and extension .jar -->
|
||||
<fileSets>
|
||||
|
||||
<!-- Include licenses -->
|
||||
<fileSet>
|
||||
<outputDirectory></outputDirectory>
|
||||
<directory>target/licenses</directory>
|
||||
</fileSet>
|
||||
|
||||
<!-- Include extension .jar -->
|
||||
<fileSet>
|
||||
<directory>target</directory>
|
||||
<outputDirectory></outputDirectory>
|
||||
<includes>
|
||||
<include>*.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
|
||||
</fileSets>
|
||||
|
||||
</assembly>
|
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* The current status of an authentication failure, including the number of
|
||||
* times the failure has occurred.
|
||||
*/
|
||||
public class AuthenticationFailureStatus {
|
||||
|
||||
/**
|
||||
* The timestamp of the last authentication failure, as returned by
|
||||
* System.nanoTime().
|
||||
*/
|
||||
private long lastFailure;
|
||||
|
||||
/**
|
||||
* The number of failures that have occurred.
|
||||
*/
|
||||
private final AtomicInteger failureCount;
|
||||
|
||||
/**
|
||||
* The maximum number of failures that may occur before the user/address
|
||||
* causing the failures is blocked.
|
||||
*/
|
||||
private final int maxAttempts;
|
||||
|
||||
/**
|
||||
* The amount of time that a user/address must remain blocked after they
|
||||
* have reached the maximum number of failures. Unlike the value provided
|
||||
* at construction time, this value is maintained in nanoseconds.
|
||||
*/
|
||||
private final long duration;
|
||||
|
||||
/**
|
||||
* Creates an AuthenticationFailureStatus that represents a single failure
|
||||
* and is subject to the given restrictions. Additional failures may be
|
||||
* flagged after creation with {@link #notifyFailed()}.
|
||||
*
|
||||
* @param maxAttempts
|
||||
* The maximum number of failures that may occur before the
|
||||
* user/address causing the failures is blocked.
|
||||
*
|
||||
* @param duration
|
||||
* The amount of time, in seconds, that a user/address must remain
|
||||
* blocked after they have reached the maximum number of failures.
|
||||
*/
|
||||
public AuthenticationFailureStatus(int maxAttempts, int duration) {
|
||||
this.lastFailure = System.nanoTime();
|
||||
this.failureCount = new AtomicInteger(1);
|
||||
this.maxAttempts = maxAttempts;
|
||||
this.duration = TimeUnit.SECONDS.toNanos(duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this authentication failure, noting that the failure it
|
||||
* represents has recurred.
|
||||
*/
|
||||
public void notifyFailed() {
|
||||
lastFailure = System.nanoTime();
|
||||
failureCount.incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this authentication failure is recent enough that it
|
||||
* should still be tracked. This function will return false for
|
||||
* authentication failures that have not recurred for at least the duration
|
||||
* provided at construction time.
|
||||
*
|
||||
* @return
|
||||
* true if this authentication failure is recent enough that it should
|
||||
* still be tracked, false otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return System.nanoTime() - lastFailure <= duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the user/address causing this authentication failure
|
||||
* should be blocked based on the restrictions provided at construction
|
||||
* time.
|
||||
*
|
||||
* @return
|
||||
* true if the user/address causing this failure should be blocked,
|
||||
* false otherwise.
|
||||
*/
|
||||
public boolean isBlocked() {
|
||||
return isValid() && failureCount.get() >= maxAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of authentication failures that have been
|
||||
* recorded through creating this object and invoking
|
||||
* {@link #notifyFailed()}.
|
||||
*
|
||||
* @return
|
||||
* The total number of failures that have occurred.
|
||||
*/
|
||||
public int getFailures() {
|
||||
return failureCount.get();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.guacamole.GuacamoleClientTooManyException;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.net.auth.Credentials;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Provides automated tracking and blocking of IP addresses that repeatedly
|
||||
* fail authentication.
|
||||
*/
|
||||
public class AuthenticationFailureTracker {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthenticationFailureTracker.class);
|
||||
|
||||
/**
|
||||
* All authentication failures currently being tracked, stored by the
|
||||
* associated IP address.
|
||||
*/
|
||||
private final ConcurrentMap<String, AuthenticationFailureStatus> failures =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* The maximum number of failed authentication attempts allowed before an
|
||||
* address is temporarily banned.
|
||||
*/
|
||||
private final int maxAttempts;
|
||||
|
||||
/**
|
||||
* The length of time that each address should be banned after reaching the
|
||||
* maximum number of failed authentication attempts, in seconds.
|
||||
*/
|
||||
private final int banDuration;
|
||||
|
||||
/**
|
||||
* Creates a new AuthenticationFailureTracker that automatically blocks
|
||||
* authentication attempts based on the provided blocking criteria.
|
||||
*
|
||||
* @param maxAttempts
|
||||
* The maximum number of failed authentication attempts allowed before
|
||||
* an address is temporarily banned.
|
||||
*
|
||||
* @param banDuration
|
||||
* The length of time that each address should be banned after reaching
|
||||
* the maximum number of failed authentication attempts, in seconds.
|
||||
*/
|
||||
public AuthenticationFailureTracker(int maxAttempts, int banDuration) {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given Credentials do not contain any specific
|
||||
* authentication parameters, including HTTP parameters. An authentication
|
||||
* request that contains no parameters whatsoever will tend to be the
|
||||
* first, anonymous, credential-less authentication attempt that results in
|
||||
* the initial login screen rendering.
|
||||
*
|
||||
* @param credentials
|
||||
* The Credentials object to test.
|
||||
*
|
||||
* @return
|
||||
* true if the given Credentials contain no authentication parameters
|
||||
* whatsoever, false otherwise.
|
||||
*/
|
||||
private boolean isEmpty(Credentials credentials) {
|
||||
|
||||
// An authentication request that contains an explicit username or
|
||||
// password (even if blank) is non-empty, regardless of how the values
|
||||
// were passed
|
||||
if (credentials.getUsername() != null || credentials.getPassword() != null)
|
||||
return false;
|
||||
|
||||
// All further tests depend on HTTP request details
|
||||
HttpServletRequest request = credentials.getRequest();
|
||||
if (request == null)
|
||||
return true;
|
||||
|
||||
// An authentication request is non-empty if it contains any HTTP
|
||||
// parameters at all or contains an authentication token
|
||||
return !request.getParameterNames().hasMoreElements()
|
||||
&& request.getHeader("Guacamole-Token") == null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that the given address has just failed to authenticate and
|
||||
* returns the AuthenticationFailureStatus that represents that failure. If
|
||||
* the address isn't already being tracked, it will begin being tracked as
|
||||
* of this call. If the address is already tracked, the returned
|
||||
* AuthenticationFailureStatus will represent past authentication failures,
|
||||
* as well.
|
||||
*
|
||||
* @param address
|
||||
* The address that has failed to authenticate.
|
||||
*
|
||||
* @return
|
||||
* An AuthenticationFailureStatus that represents this latest
|
||||
* authentication failure for the given address, as well as any past
|
||||
* failures.
|
||||
*/
|
||||
private AuthenticationFailureStatus getAuthenticationFailure(String address) {
|
||||
|
||||
AuthenticationFailureStatus newFailure = new AuthenticationFailureStatus(maxAttempts, banDuration);
|
||||
AuthenticationFailureStatus status = failures.putIfAbsent(address, newFailure);
|
||||
if (status == null)
|
||||
return newFailure;
|
||||
|
||||
status.notifyFailed();
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that an authentication request has been received, as well as
|
||||
* whether that request is known to have failed. If the associated address
|
||||
* is currently being blocked, an exception will be thrown.
|
||||
*
|
||||
* @param credentials
|
||||
* The credentials associated with the authentication request.
|
||||
*
|
||||
* @param failed
|
||||
* Whether the request is known to have failed. If the status of the
|
||||
* request is not yet known, this should be false.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If the authentication request is being blocked due to brute force
|
||||
* prevention rules.
|
||||
*/
|
||||
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;
|
||||
|
||||
// Determine originating address of the authentication request
|
||||
String address = credentials.getRemoteAddress();
|
||||
if (address == null)
|
||||
throw new GuacamoleServerException("Source address cannot be determined.");
|
||||
|
||||
// Get current failure status for the address associated with the
|
||||
// authentication request, adding/updating that status if the request
|
||||
// was itself a failure
|
||||
AuthenticationFailureStatus status;
|
||||
if (failed) {
|
||||
status = getAuthenticationFailure(address);
|
||||
logger.debug("Authentication has failed for address \"{}\" (current total failures: {}/{}).",
|
||||
address, status.getFailures(), maxAttempts);
|
||||
}
|
||||
else
|
||||
status = failures.get(address);
|
||||
|
||||
if (status != null) {
|
||||
|
||||
// Explicitly block further processing of authentication/authorization
|
||||
// if too many failures have occurred
|
||||
if (status.isBlocked()) {
|
||||
logger.debug("Blocking authentication attempt from address \"{}\" due to number of authentication failures.", address);
|
||||
throw new GuacamoleClientTooManyException("Too many failed "
|
||||
+ "authentication attempts. Please try again later.");
|
||||
}
|
||||
|
||||
// Clean up tracking of failures if the address is no longer
|
||||
// relevant (all failures are sufficiently old)
|
||||
else if (!status.isValid()) {
|
||||
logger.debug("Removing address \"{}\" from tracking as there are no recent authentication failures.", address);
|
||||
failures.remove(address);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public void notifyAuthenticationFailed(Credentials credentials)
|
||||
throws GuacamoleException {
|
||||
notifyAuthenticationStatus(credentials, true);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
|
||||
import org.apache.guacamole.net.event.AuthenticationFailureEvent;
|
||||
import org.apache.guacamole.net.event.AuthenticationSuccessEvent;
|
||||
import org.apache.guacamole.net.event.listener.Listener;
|
||||
|
||||
/**
|
||||
* Listener implementation which automatically tracks authentication failures
|
||||
* such that further authentication attempts may be automatically blocked by
|
||||
* {@link BanningAuthenticationProvider} if they match configured criteria.
|
||||
*/
|
||||
public class BanningAuthenticationListener implements Listener {
|
||||
|
||||
/**
|
||||
* Shared tracker of addresses that have repeatedly failed authentication.
|
||||
*/
|
||||
private static AuthenticationFailureTracker tracker;
|
||||
|
||||
/**
|
||||
* Assigns the shared tracker instance used by both the {@link BanningAuthenticationProvider}
|
||||
* and this listener. This function MUST be invoked with the tracker
|
||||
* created for BanningAuthenticationProvider as soon as possible (during
|
||||
* construction of BanningAuthenticationProvider), or processing of
|
||||
* received events will fail internally.
|
||||
*
|
||||
* @param tracker
|
||||
* The tracker instance to use for received authentication events.
|
||||
*/
|
||||
public static void setAuthenticationFailureTracker(AuthenticationFailureTracker tracker) {
|
||||
BanningAuthenticationListener.tracker = tracker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEvent(Object event) throws GuacamoleException {
|
||||
|
||||
if (event instanceof AuthenticationFailureEvent) {
|
||||
|
||||
AuthenticationFailureEvent failure = (AuthenticationFailureEvent) event;
|
||||
|
||||
// Requests for additional credentials are not failures per se,
|
||||
// but continuations of a multi-request authentication attempt that
|
||||
// has not yet succeeded OR failed
|
||||
if (failure.getFailure() instanceof GuacamoleInsufficientCredentialsException) {
|
||||
tracker.notifyAuthenticationRequestReceived(failure.getCredentials());
|
||||
return;
|
||||
}
|
||||
|
||||
// Consider all other errors to be failed auth attempts
|
||||
tracker.notifyAuthenticationFailed(failure.getCredentials());
|
||||
|
||||
}
|
||||
|
||||
else if (event instanceof AuthenticationSuccessEvent) {
|
||||
AuthenticationSuccessEvent success = (AuthenticationSuccessEvent) event;
|
||||
tracker.notifyAuthenticationSuccess(success.getCredentials());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.environment.Environment;
|
||||
import org.apache.guacamole.environment.LocalEnvironment;
|
||||
import org.apache.guacamole.net.auth.AbstractAuthenticationProvider;
|
||||
import org.apache.guacamole.net.auth.AuthenticatedUser;
|
||||
import org.apache.guacamole.net.auth.Credentials;
|
||||
import org.apache.guacamole.net.auth.UserContext;
|
||||
import org.apache.guacamole.properties.IntegerGuacamoleProperty;
|
||||
|
||||
/**
|
||||
* AuthenticationProvider implementation that blocks further authentication
|
||||
* attempts that are related to past authentication failures flagged by
|
||||
* {@link BanningAuthenticationListener}.
|
||||
*/
|
||||
public class BanningAuthenticationProvider extends AbstractAuthenticationProvider {
|
||||
|
||||
/**
|
||||
* The maximum number of failed authentication attempts allowed before an
|
||||
* address is temporarily banned.
|
||||
*/
|
||||
private static final IntegerGuacamoleProperty MAX_ATTEMPTS = new IntegerGuacamoleProperty() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ban-max-invalid-attempts";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The length of time that each address should be banned after reaching the
|
||||
* maximum number of failed authentication attempts, in seconds.
|
||||
*/
|
||||
private static final IntegerGuacamoleProperty IP_BAN_DURATION = new IntegerGuacamoleProperty() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ban-address-duration";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The default maximum number of failed authentication attempts allowed
|
||||
* before an address is temporarily banned.
|
||||
*/
|
||||
private static final int DEFAULT_MAX_ATTEMPTS = 5;
|
||||
|
||||
/**
|
||||
* The default length of time that each address should be banned after
|
||||
* reaching the maximum number of failed authentication attempts, in
|
||||
* seconds.
|
||||
*/
|
||||
private static final int DEFAULT_IP_BAN_DURATION = 300;
|
||||
|
||||
/**
|
||||
* Shared tracker of addresses that have repeatedly failed authentication.
|
||||
*/
|
||||
private final AuthenticationFailureTracker tracker;
|
||||
|
||||
/**
|
||||
* Creates a new BanningAuthenticationProvider which automatically bans
|
||||
* further authentication attempts from addresses that have repeatedly
|
||||
* failed to authenticate. The ban duration and maximum number of failed
|
||||
* attempts allowed before banning are configured within
|
||||
* guacamole.properties.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs parsing the configuration properties used by this
|
||||
* extension.
|
||||
*/
|
||||
public BanningAuthenticationProvider() throws GuacamoleException {
|
||||
|
||||
Environment environment = LocalEnvironment.getInstance();
|
||||
int maxAttempts = environment.getProperty(MAX_ATTEMPTS, DEFAULT_MAX_ATTEMPTS);
|
||||
int banDuration = environment.getProperty(IP_BAN_DURATION, DEFAULT_IP_BAN_DURATION);
|
||||
|
||||
tracker = new AuthenticationFailureTracker(maxAttempts, banDuration);
|
||||
BanningAuthenticationListener.setAuthenticationFailureTracker(tracker);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return "ban";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticatedUser authenticateUser(Credentials credentials) throws GuacamoleException {
|
||||
tracker.notifyAuthenticationRequestReceived(credentials);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserContext getUserContext(AuthenticatedUser authenticatedUser) throws GuacamoleException {
|
||||
tracker.notifyAuthenticationRequestReceived(authenticatedUser.getCredentials());
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
|
||||
"guacamoleVersion" : "1.4.0",
|
||||
|
||||
"name" : "Brute-force Authentication Detection/Prevention",
|
||||
"namespace" : "ban",
|
||||
|
||||
"authProviders" : [
|
||||
"org.apache.guacamole.auth.ban.BanningAuthenticationProvider"
|
||||
],
|
||||
|
||||
"listeners" : [
|
||||
"org.apache.guacamole.auth.ban.BanningAuthenticationListener"
|
||||
]
|
||||
|
||||
}
|
Reference in New Issue
Block a user