All Downloads are FREE. Search and download functionalities are using the official Maven repository.

host.anzo.core.service.FirewallService Maven / Gradle / Ivy

There is a newer version: 1.29
Show newest version
package host.anzo.core.service;

import com.google.common.util.concurrent.RateLimiter;
import host.anzo.commons.model.enums.EFirewallType;
import host.anzo.commons.model.enums.ERestrictionType;
import host.anzo.commons.utils.DateTimeUtils;
import host.anzo.commons.utils.NetworkUtils;
import host.anzo.commons.utils.VMUtils;
import host.anzo.core.config.FirewallConfig;
import host.anzo.core.startup.Scheduled;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SystemUtils;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author ANZO
 */
@Slf4j(topic = "Firewall")
@SuppressWarnings("UnstableApiUsage")
public class FirewallService {
    @Getter(lazy = true)
    private static final FirewallService instance = new FirewallService();

    private final Map blockedIps = new ConcurrentHashMap<>();
    private final Map> connectionRateLimiters = new ConcurrentHashMap<>();

    private FirewallService() {
        flushSystemFirewall();
    }

    /**
     * @param clazz service class
     * @param ip client IP address
     * @param destPort destination service port
     * @param allowedRequestsPerSecond maximum allowed requests per second
     * @param restrictionType restriction, applied to connection exceeded rate limit
     * @return {@code true} if specified IP allowed to connect/request specified service clazz, {@code false} otherwise
     */
    public boolean isAllowedAddress(Class clazz, String ip, int destPort, double allowedRequestsPerSecond, ERestrictionType restrictionType) {
        return isAllowedAddress(clazz.getSimpleName(), ip, destPort, allowedRequestsPerSecond, restrictionType);
    }

    /**
     * @param className service class name
     * @param ip client IP address
     * @param destPort destination service port
     * @param allowedRequestsPerSecond maximum allowed requests per second
     * @param restrictionType restriction, applied to connection exceeded rate limit
     * @return {@code true} if specified IP allowed to connect/request specified service clazz, {@code false} otherwise
     */
    public boolean isAllowedAddress(String className, String ip, int destPort, double allowedRequestsPerSecond, ERestrictionType restrictionType) {
        if (!VMUtils.DEBUG) {
            try {
                if (NetworkUtils.isLocalAddress(InetAddress.getByName(ip))) {
                    return true;
                }
            }
            catch (UnknownHostException ignored) {
            }
        }

        if (blockedIps.containsKey(ip)) {
            return false;
        }

        final Map classLimiters = connectionRateLimiters.computeIfAbsent(className, k -> new ConcurrentHashMap<>());
        final RateLimiter ipLimiter = classLimiters.computeIfAbsent(ip, k -> RateLimiter.create(allowedRequestsPerSecond));
        // Check connection per second rule
        if (!ipLimiter.tryAcquire()) {
            if (restrictionType == ERestrictionType.BAN) {
                addBlock(className, ip, destPort, FirewallConfig.FIREWALL_BAN_TIME, TimeUnit.MILLISECONDS);
            }
            classLimiters.remove(ip);
            return false;
        }
        return true;
    }

    /**
     * Add firewall block rule for specified parameters
     * @param className service class name
     * @param ip client IP address
     * @param destPort destination service port
     * @param banTime ban time in specified time unit's
     * @param banTimeUnit ban time unit's
     */
    public void addBlock(String className, String ip, int destPort, long banTime, TimeUnit banTimeUnit) {
        long unban_time = 0;
        if (FirewallConfig.FIREWALL_TYPE == EFirewallType.SYSTEM && SystemUtils.IS_OS_LINUX) {
            final String firewallCommand = FirewallConfig.FIREWALL_SYSTEM_FIREWALL_RULE.replace("$ip", ip);
            try {
                Runtime.getRuntime().exec(firewallCommand.split(" "));
                unban_time = -1;
            } catch (IOException e) {
                log.error("Error while adding firewall rule for class=[{}] and ipAddress=[{}]", className, ip, e);
            }
        }
        else if (FirewallConfig.FIREWALL_TYPE == EFirewallType.INTERNAL) {
            // Block address internally for a specified time
            unban_time = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(banTime, banTimeUnit);
            blockedIps.put(ip, unban_time);
        }

        if (unban_time != 0) {
            if (unban_time > 0) {
                log.error("Address ip=[{}] blocked by [{}] firewall at port [{}] for [{}]", ip, className, destPort, DateTimeUtils.getLocalDateTime(unban_time).format(DateTimeFormatter.ISO_LOCAL_DATE));
            } else {
                log.error("Address ip=[{}] blocked by [{}] firewall at port [{}] permanently", ip, className, destPort);
            }
        }
    }

    /**
     * Remove specified IP address from internal firewall
     * @param ipAddress IP address to remove
     */
    public void removeBlock(String ipAddress) {
        blockedIps.remove(ipAddress);
    }

    /**
     * Clear blocked IP list
     */
    public void clear() {
        blockedIps.clear();
    }

    public void flushSystemFirewall() {
        if (FirewallConfig.FIREWALL_TYPE == EFirewallType.SYSTEM && SystemUtils.IS_OS_LINUX) {
            for (String set : FirewallConfig.FIREWALL_FLUSHED_SETS_BEFORE_START) {
                final String firewallCommand = "nft flush set inet filter " + set;
                try {
                    Runtime.getRuntime().exec(firewallCommand.split(" "));
                } catch (IOException e) {
                    log.error("Error while flushing firewall set [{}]", set, e);
                }
            }
        }
    }

    @SuppressWarnings("unused")
    @Scheduled(period = 1, timeUnit = TimeUnit.MINUTES, runAfterServerStart = true)
    public void cleanupBans() {
        for (Map.Entry entry : blockedIps.entrySet()) {
            if (entry.getValue() < System.currentTimeMillis()) {
                blockedIps.remove(entry.getKey());
            }
        }
        connectionRateLimiters.clear();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy