/* * Copyright 2011 NCHOVY * * Licensed 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.krakenapps.firewall.api; import java.net.InetAddress; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FirewallGroup { private final Logger logger = LoggerFactory.getLogger(FirewallGroup.class.getName()); private FirewallController controller; private String name; private Set<String> instanceNames; private ConcurrentMap<InetAddress, FirewallRule> blockedSources; private Set<FirewallGroupListener> callbacks; public FirewallGroup(FirewallController controller, String name) { this(controller, name, new ArrayList<String>(), new HashMap<InetAddress, FirewallRule>()); } public FirewallGroup(FirewallController controller, String name, Collection<String> instanceNames, Map<InetAddress, FirewallRule> blockedSources) { this.controller = controller; this.name = name; this.instanceNames = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); this.instanceNames.addAll(instanceNames); this.blockedSources = new ConcurrentHashMap<InetAddress, FirewallRule>(blockedSources); this.callbacks = Collections.newSetFromMap(new ConcurrentHashMap<FirewallGroupListener, Boolean>()); } public String getName() { return name; } public Collection<FirewallRule> getRules() { return new ArrayList<FirewallRule>(blockedSources.values()); } public FirewallRule getRule(InetAddress ip) { return blockedSources.get(ip); } public boolean hasRule(InetAddress ip) { return blockedSources.containsKey(ip); } public void blockSourceIp(InetAddress ip) { Date expire = new Date(Long.MAX_VALUE); blockSourceIp(ip, expire); } public void blockSourceIp(InetAddress ip, int minutes) { Calendar c = Calendar.getInstance(); c.add(Calendar.MINUTE, minutes); Date expire = c.getTime(); blockSourceIp(ip, expire); } public void blockSourceIp(InetAddress ip, Date expire) { if (ip == null) throw new IllegalArgumentException("ip should not be null"); FirewallRule rule = new FirewallRule(ip, expire); blockedSources.put(ip, rule); for (String instanceName : instanceNames) { logger.trace("kraken firewall api: add {} rule to {} instance", ip.getHostAddress(), instanceName); blockSourceIp(instanceName, ip); } for (FirewallGroupListener callback : callbacks) { try { callback.onBlock(this, rule); } catch (Exception e) { logger.warn("kraken firewall api: firewall group listener should not throw any exception", e); } } } private void blockSourceIp(String instanceName, InetAddress ip) { FirewallInstance instance = controller.getInstance(instanceName); if (instance == null) return; try { instance.blockSourceIp(ip); } catch (Exception e) { logger.warn("kraken firewall api: cannot block source ip " + ip.getHostAddress() + " to instance " + instanceName, e); } } public void unblockSourceIp(InetAddress ip) { if (ip == null) throw new IllegalArgumentException("ip should not be null"); FirewallRule rule = blockedSources.remove(ip); if (rule == null) return; for (String instanceName : instanceNames) { unblockSourceIp(instanceName, ip); } for (FirewallGroupListener callback : callbacks) { try { callback.onUnblock(this, rule); } catch (Exception e) { logger.warn("kraken firewall api: firewall group listener should not throw any exception", e); } } } private void unblockSourceIp(String instanceName, InetAddress ip) { FirewallInstance instance = controller.getInstance(instanceName); if (instance == null) return; try { if (!hasAlternativeRule(instanceName, ip)) instance.unblockSourceIp(ip); } catch (Exception e) { logger.warn("kraken firewall api: cannot unblock source ip " + ip.getHostAddress() + " from instance " + instanceName, e); } } public Collection<String> getMembers() { return Collections.unmodifiableCollection(instanceNames); } public boolean isMember(String instanceName) { return instanceNames.contains(instanceName); } public void join(String instanceName) { FirewallInstance instance = controller.getInstance(instanceName); if (instance == null) throw new IllegalStateException(instanceName + " instance not found"); if (!instanceNames.add(instanceName)) { logger.trace("kraken firewall api: instance {} is already member of {}", instanceName, name); return; } applyBlacklist(instance); for (FirewallGroupListener callback : callbacks) { try { callback.onJoin(this, instanceName); } catch (Exception e) { logger.warn("kraken firewall api: firewall group listener should not throw any exception", e); } } } private void applyBlacklist(FirewallInstance instance) { logger.trace("kraken firewall api: applying [{}] blacklist to instance {}, rule count {}", new Object[] { getName(), instance.getName(), getRules().size() }); // apply blocked source to this instance for (FirewallRule rule : getRules()) { instance.blockSourceIp(rule.getSourceIp()); } } public void leave(String instanceName) { if (controller.getInstance(instanceName) == null) throw new IllegalStateException(instanceName + " instance not found"); if (!instanceNames.remove(instanceName)) return; for (FirewallRule rule : blockedSources.values()) { if (!hasAlternativeRule(instanceName, rule.getSourceIp())) unblockSourceIp(instanceName, rule.getSourceIp()); } for (FirewallGroupListener callback : callbacks) { try { callback.onLeave(this, instanceName); } catch (Exception e) { logger.warn("kraken firewall api: firewall group listener should not throw any exception", e); } } } private boolean hasAlternativeRule(String instanceName, InetAddress ip) { for (FirewallGroup group : controller.getGroups()) { if (group == this) continue; if (group.isMember(instanceName) && group.hasRule(ip)) return true; } return false; } public void onLoad(FirewallInstanceManager manager, FirewallInstance instance) { instanceNames.add(instance.getName()); applyBlacklist(instance); } public void onUnload(FirewallInstanceManager manager, FirewallInstance instance) { instanceNames.remove(instance.getName()); } public void addEventListener(FirewallGroupListener callback) { if (callback == null) throw new IllegalArgumentException("firewall group listener should be not null"); callbacks.add(callback); } public void removeEventListener(FirewallGroupListener callback) { if (callback == null) throw new IllegalArgumentException("firewall group listener should be not null"); callbacks.remove(callback); } @Override public String toString() { return String.format("name=%s, rules=%d", name, blockedSources.size()); } }