Sentinel.java
package emissary.core.sentinel;
import emissary.config.ConfigUtil;
import emissary.config.Configurator;
import emissary.core.Namespace;
import emissary.core.NamespaceException;
import emissary.core.sentinel.protocols.Protocol;
import emissary.core.sentinel.protocols.ProtocolFactory;
import org.apache.commons.lang3.ThreadUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Track mobile agents and take action on suspicious behavior
*/
public class Sentinel implements Runnable {
protected static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static final String DEFAULT_NAMESPACE_NAME = "Sentinel";
// protocols contain an action to perform when the set of rule conditions are met
protected final Set<Protocol> protocols = new LinkedHashSet<>();
// the default configuration Sentinel.cfg
protected Configurator config;
// how many minutes to sleep before checking protocols
protected long pollingInterval = 5;
// Loop control
protected boolean timeToQuit = false;
// turn on/off sentinel
protected boolean enabled = false;
/**
* Create a Sentinel - set it running and bind into the {@link Namespace}
*/
@SuppressWarnings("ThreadPriorityCheck")
public Sentinel() {
configure();
if (this.enabled) {
final Thread thread = new Thread(this, DEFAULT_NAMESPACE_NAME);
thread.setPriority(Thread.NORM_PRIORITY);
thread.setDaemon(true);
Namespace.bind(DEFAULT_NAMESPACE_NAME, this);
thread.start();
} else {
logger.info("Sentinel is disabled");
}
}
/**
* Start up the Sentinel thread
*/
public static void start() {
new Sentinel();
}
/**
* Lookup the default Sentinel in the {@link Namespace}
*
* @return The registered Sentinel
*/
public static Sentinel lookup() throws NamespaceException {
return (Sentinel) Namespace.lookup(DEFAULT_NAMESPACE_NAME);
}
/**
* Get the currently configured polling interval
*
* @return the currently configured polling interval
*/
public long getPollingInterval() {
return pollingInterval;
}
/**
* Safely stop the monitoring Thread
*/
public void quit() {
logger.info("Stopping Sentinel...");
this.timeToQuit = true;
ThreadUtils.findThreadsByName(DEFAULT_NAMESPACE_NAME).forEach(Thread::interrupt);
}
/**
* Runnable interface where we get to monitor stuff
*/
@Override
public void run() {
logger.info("Sentinel is watching");
while (!this.timeToQuit) {
// Delay this loop
try {
Thread.sleep(TimeUnit.MINUTES.toMillis(pollingInterval));
logger.debug("Sentinel is still watching");
protocols.forEach(this::run);
} catch (InterruptedException ignore) {
Thread.currentThread().interrupt();
}
}
Namespace.unbind(DEFAULT_NAMESPACE_NAME);
logger.info("Sentinel stopped");
}
protected void run(final Protocol protocol) {
try {
protocol.run();
} catch (IOException e) {
logger.error("There was an error running protocol", e);
}
}
@Override
public String toString() {
return "Watching agents with " + protocols;
}
/**
* Get the Configurator
*/
protected void configure() {
try {
this.config = ConfigUtil.getConfigInfo(Sentinel.class);
init();
} catch (IOException e) {
logger.warn("Cannot read Sentinel.cfg, taking default values");
}
}
/**
* Initialize Protocols
*/
protected void init() {
this.enabled = config.findBooleanEntry("ENABLED", false);
if (this.enabled) {
this.pollingInterval = this.config.findIntEntry("POLLING_INTERVAL_MINUTES", 5);
logger.trace("Sentinel protocols initializing...");
for (String protocolConfig : this.config.findEntries("PROTOCOL")) {
try {
Protocol protocol = ProtocolFactory.get(protocolConfig);
if (protocol.isEnabled()) {
logger.debug("Sentinel protocol initialized {}", protocol);
this.protocols.add(protocol);
} else {
logger.debug("Sentinel protocol disabled {}", protocol);
}
} catch (RuntimeException e) {
logger.warn("Unable to configure Sentinel Protocol[{}]: {}", protocolConfig, e.getMessage());
}
}
if (this.protocols.isEmpty()) {
logger.warn("Sentinel initialization failed due to no protocols found, disabling");
this.enabled = false;
} else {
logger.info("Sentinel initialized protocols {}", protocols);
}
}
}
}