Add .gitignore and .ratignore files for various directories
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
0
extensions/guacamole-auth-ban/.ratignore
Normal file
0
extensions/guacamole-auth-ban/.ratignore
Normal file
81
extensions/guacamole-auth-ban/pom.xml
Normal file
81
extensions/guacamole-auth-ban/pom.xml
Normal file
@@ -0,0 +1,81 @@
|
||||
<?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.6.0</version>
|
||||
<name>guacamole-auth-ban</name>
|
||||
<url>http://guacamole.apache.org/</url>
|
||||
|
||||
<parent>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>extensions</artifactId>
|
||||
<version>1.6.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.6.0</version>
|
||||
<scope>provided</scope>
|
||||
|
||||
<!-- Exclude transitive dependencies that will be overridden by
|
||||
newer versions required by Caffeine -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.checkerframework</groupId>
|
||||
<artifactId>checker-qual</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>error_prone_annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
|
||||
</dependency>
|
||||
|
||||
<!-- Guava Base Libraries -->
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>2.9.3</version>
|
||||
</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,200 @@
|
||||
/*
|
||||
* 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.auth.ban.status.AuthenticationFailureTracker;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.auth.ban.status.InMemoryAuthenticationFailureTracker;
|
||||
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.credentials.GuacamoleInsufficientCredentialsException;
|
||||
import org.apache.guacamole.net.event.AuthenticationFailureEvent;
|
||||
import org.apache.guacamole.net.event.AuthenticationSuccessEvent;
|
||||
import org.apache.guacamole.net.event.listener.Listener;
|
||||
import org.apache.guacamole.net.event.AuthenticationRequestReceivedEvent;
|
||||
import org.apache.guacamole.properties.IntegerGuacamoleProperty;
|
||||
import org.apache.guacamole.properties.LongGuacamoleProperty;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Listener implementation which automatically tracks authentication failures
|
||||
* such that further authentication attempts may be automatically blocked if
|
||||
* they match configured criteria.
|
||||
*/
|
||||
public class BanningAuthenticationListener implements Listener {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(BanningAuthenticationListener.class);
|
||||
|
||||
/**
|
||||
* 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 maximum number of failed authentication attempts tracked at any
|
||||
* given time. Once this number of addresses is exceeded, the oldest
|
||||
* authentication attempts are rotated off on an LRU basis.
|
||||
*/
|
||||
private static final LongGuacamoleProperty MAX_ADDRESSES = new LongGuacamoleProperty() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ban-max-addresses";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The maximum number of failed authentication attempts tracked at any
|
||||
* given time. Once this number of addresses is exceeded, the oldest
|
||||
* authentication attempts are rotated off on an LRU basis.
|
||||
*/
|
||||
private static final long DEFAULT_MAX_ADDRESSES = 10485760;
|
||||
|
||||
/**
|
||||
* Tracker of addresses that have repeatedly failed authentication.
|
||||
*/
|
||||
private final AuthenticationFailureTracker tracker;
|
||||
|
||||
/**
|
||||
* Creates a new BanningAuthenticationListener 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 BanningAuthenticationListener() 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);
|
||||
long maxAddresses = environment.getProperty(MAX_ADDRESSES, DEFAULT_MAX_ADDRESSES);
|
||||
|
||||
// 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 if (maxAddresses <= 0) {
|
||||
this.tracker = new NullAuthenticationFailureTracker();
|
||||
logger.info("Maximum number of tracked addresses has been set to "
|
||||
+ "{}. Automatic banning of brute-force authentication "
|
||||
+ "attempts will be disabled.", maxAddresses);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEvent(Object event) throws GuacamoleException {
|
||||
|
||||
// Notify auth tracker of each request received BEFORE the request is
|
||||
// processed ...
|
||||
if (event instanceof AuthenticationRequestReceivedEvent) {
|
||||
AuthenticationRequestReceivedEvent request = (AuthenticationRequestReceivedEvent) event;
|
||||
tracker.notifyAuthenticationRequestReceived(request.getCredentials());
|
||||
}
|
||||
|
||||
// ... as well as every explicit failure ...
|
||||
else 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());
|
||||
|
||||
}
|
||||
|
||||
// ... and explicit success.
|
||||
else if (event instanceof AuthenticationSuccessEvent) {
|
||||
AuthenticationSuccessEvent success = (AuthenticationSuccessEvent) event;
|
||||
tracker.notifyAuthenticationSuccess(success.getCredentials());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -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.status;
|
||||
|
||||
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 is initialized to zero
|
||||
* failures 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(0);
|
||||
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,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;
|
||||
|
||||
}
|
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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 com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.language.TranslatableGuacamoleClientTooManyException;
|
||||
import org.apache.guacamole.net.auth.Credentials;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 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 InMemoryAuthenticationFailureTracker implements AuthenticationFailureTracker {
|
||||
|
||||
/**
|
||||
* Logger for this class.
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(InMemoryAuthenticationFailureTracker.class);
|
||||
|
||||
/**
|
||||
* All authentication failures currently being tracked, stored by the
|
||||
* associated IP address.
|
||||
*/
|
||||
private final Cache<String, AuthenticationFailureStatus> failures;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param maxAddresses
|
||||
* The maximum number of unique IP addresses that should be tracked
|
||||
* before discarding older tracked failures.
|
||||
*/
|
||||
public InMemoryAuthenticationFailureTracker(int maxAttempts, int banDuration,
|
||||
long maxAddresses) {
|
||||
|
||||
this.maxAttempts = maxAttempts;
|
||||
this.banDuration = banDuration;
|
||||
|
||||
// Limit maximum number of tracked addresses to configured upper bound
|
||||
this.failures = Caffeine.newBuilder()
|
||||
.maximumSize(maxAddresses)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 status = failures.get(address,
|
||||
(addr) -> new AuthenticationFailureStatus(maxAttempts, banDuration));
|
||||
|
||||
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 {
|
||||
|
||||
// Ignore requests that do not contain explicit parameters of any kind
|
||||
if (credentials.isEmpty())
|
||||
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.info("Authentication has failed for address \"{}\" (current total failures: {}/{}).",
|
||||
address, status.getFailures(), maxAttempts);
|
||||
}
|
||||
else
|
||||
status = failures.getIfPresent(address);
|
||||
|
||||
if (status != null) {
|
||||
|
||||
// Explicitly block further processing of authentication/authorization
|
||||
// if too many failures have occurred
|
||||
if (status.isBlocked()) {
|
||||
logger.warn("Blocking authentication attempt from address \"{}\" due to number of authentication failures.", address);
|
||||
throw new TranslatableGuacamoleClientTooManyException("Too "
|
||||
+ "many failed authentication attempts.",
|
||||
"LOGIN.ERROR_TOO_MANY_ATTEMPTS");
|
||||
}
|
||||
|
||||
// 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.invalidate(address);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyAuthenticationRequestReceived(Credentials credentials)
|
||||
throws GuacamoleException {
|
||||
notifyAuthenticationStatus(credentials, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyAuthenticationSuccess(Credentials credentials)
|
||||
throws GuacamoleException {
|
||||
notifyAuthenticationStatus(credentials, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyAuthenticationFailed(Credentials credentials)
|
||||
throws GuacamoleException {
|
||||
notifyAuthenticationStatus(credentials, true);
|
||||
}
|
||||
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
{
|
||||
|
||||
"guacamoleVersion" : "1.6.0",
|
||||
|
||||
"name" : "Brute-force Authentication Detection/Prevention",
|
||||
"namespace" : "ban",
|
||||
|
||||
"listeners" : [
|
||||
"org.apache.guacamole.auth.ban.BanningAuthenticationListener"
|
||||
],
|
||||
|
||||
"translations" : [
|
||||
"translations/en.json",
|
||||
"translations/pl.json"
|
||||
]
|
||||
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"LOGIN": {
|
||||
"ERROR_TOO_MANY_ATTEMPTS" : "Too many failed authentication attempts. Please try again later."
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"LOGIN": {
|
||||
"ERROR_TOO_MANY_ATTEMPTS" : "Zbyt wiele nieudanych prób logowania. Spróbuj ponownie później."
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user