/* * 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.siem.engine; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.felix.ipojo.annotations.Component; import org.apache.felix.ipojo.annotations.Invalidate; import org.apache.felix.ipojo.annotations.Provides; import org.apache.felix.ipojo.annotations.Requires; import org.apache.felix.ipojo.annotations.Validate; import org.krakenapps.api.PrimitiveConverter; import org.krakenapps.confdb.Config; import org.krakenapps.confdb.ConfigCollection; import org.krakenapps.confdb.ConfigDatabase; import org.krakenapps.confdb.ConfigIterator; import org.krakenapps.confdb.Predicates; import org.krakenapps.log.api.Log; import org.krakenapps.log.api.LogNormalizer; import org.krakenapps.log.api.LogNormalizerFactory; import org.krakenapps.log.api.LogNormalizerFactoryRegistry; import org.krakenapps.log.api.LogParser; import org.krakenapps.log.api.LogParserFactory; import org.krakenapps.log.api.LogParserFactoryRegistry; import org.krakenapps.log.api.LogPipe; import org.krakenapps.log.api.Logger; import org.krakenapps.log.api.LoggerRegistry; import org.krakenapps.log.api.LoggerRegistryEventListener; import org.krakenapps.logstorage.LogStorage; import org.krakenapps.logstorage.LogTableRegistry; import org.krakenapps.siem.ConfigManager; import org.krakenapps.siem.LogServer; import org.krakenapps.siem.NormalizedLog; import org.krakenapps.siem.NormalizedLogListener; import org.krakenapps.siem.model.ManagedLogger; @Component(name = "siem-log-server") @Provides public class LogServerEngine implements LogServer, LogPipe, LoggerRegistryEventListener { private final org.slf4j.Logger slog = org.slf4j.LoggerFactory.getLogger(LogServerEngine.class.getName()); @Requires private ConfigManager configManager; @Requires private LoggerRegistry loggerRegistry; @Requires private LogParserFactoryRegistry parserFactoryRegistry; @Requires private LogNormalizerFactoryRegistry normalizerFactoryRegistry; @Requires private LogTableRegistry logTableRegistry; @Requires private LogStorage logStorage; private ConcurrentMap<String, CopyOnWriteArrayList<NormalizedLogListener>> normalizedLogCallbacks; /** * logger fullname to managed logger mappings. you should RELOAD server * after logger metadata update */ private ConcurrentMap<String, ManagedLogger> managedLoggers; /** * logger fullname to log parser mappings */ private ConcurrentMap<String, LogParser> parsers; /** * logger fullname to log normalizer mappings */ private ConcurrentMap<String, LogNormalizer> normalizers; @Validate public void start() { normalizedLogCallbacks = new ConcurrentHashMap<String, CopyOnWriteArrayList<NormalizedLogListener>>(); managedLoggers = new ConcurrentHashMap<String, ManagedLogger>(); parsers = new ConcurrentHashMap<String, LogParser>(); normalizers = new ConcurrentHashMap<String, LogNormalizer>(); // add logger registration monitor loggerRegistry.addListener(this); // get all managed loggers and connect log pipes for (ManagedLogger m : getManagedLoggers()) { Logger logger = loggerRegistry.getLogger(m.getFullName()); if (logger == null) continue; connectManagedLogger(logger); } } @Invalidate public void stop() { if (loggerRegistry == null) return; // disconnect all pipes for (Logger logger : loggerRegistry.getLoggers()) disconnectManagedLogger(logger); loggerRegistry.removeListener(this); } @Override public void write(org.krakenapps.logstorage.Log log) { logStorage.write(log); } @Override public ManagedLogger getManagedLogger(String fullName) { ConfigCollection col = getCol(); Config c = col.findOne(Predicates.field("full_name", fullName)); if (c == null) return null; return PrimitiveConverter.parse(ManagedLogger.class, c.getDocument()); } @Override public Collection<ManagedLogger> getManagedLoggers() { ConfigCollection col = getCol(); ConfigIterator it = col.findAll(); try { List<ManagedLogger> loggers = new ArrayList<ManagedLogger>(); while (it.hasNext()) loggers.add(PrimitiveConverter.parse(ManagedLogger.class, it.next().getDocument())); return loggers; } finally { if (it != null) it.close(); } } @Override public void createManagedLogger(ManagedLogger ml) { Logger logger = loggerRegistry.getLogger(ml.getFullName()); if (logger == null) throw new IllegalStateException("logger not found: " + ml.getFullName()); // check if duplicated managed logger exists ConfigCollection col = getCol(); Config c = col.findOne(Predicates.field("full_name", ml.getFullName())); if (c != null) throw new IllegalStateException("duplicated managed logger exists: " + ml.getFullName()); col.add(PrimitiveConverter.serialize(ml)); // create log table logStorage.createTable(ml.getFullName(), ml.getMetadata()); // connect pipe connectManagedLogger(logger); } @Override public void removeManagedLogger(ManagedLogger ml) { ConfigCollection col = getCol(); Config c = col.findOne(Predicates.field("full_name", ml.getFullName())); if (c == null) return; // disconnect pipe Logger logger = loggerRegistry.getLogger(ml.getFullName()); if (logger != null) disconnectManagedLogger(logger); // remove configuration col.remove(c); // drop log table logStorage.dropTable(ml.getFullName()); } @Override public void addNormalizedLogListener(String category, NormalizedLogListener callback) { CopyOnWriteArrayList<NormalizedLogListener> callbacks = new CopyOnWriteArrayList<NormalizedLogListener>(); CopyOnWriteArrayList<NormalizedLogListener> old = normalizedLogCallbacks.putIfAbsent(category, callbacks); if (old != null) callbacks = old; callbacks.add(callback); } @Override public void removeNormalizedLogListener(String category, NormalizedLogListener callback) { CopyOnWriteArrayList<NormalizedLogListener> callbacks = normalizedLogCallbacks.get(category); if (callbacks != null) callbacks.remove(callback); } // // LogPipe callbacks // @Override public void onLog(Logger logger, Log log) { String fullName = logger.getFullName(); logStorage.write(convert(fullName, log)); ManagedLogger ml = managedLoggers.get(fullName); if (ml == null) { slog.trace("kraken siem: managed logger not found, {}", fullName); return; } LogParser parser = parsers.get(fullName); if (parser == null) { slog.trace("kraken siem: parser not found for logger [{}]", fullName); return; } Map<String, Object> parsed = parser.parse(log.getParams()); if (parsed == null) { slog.debug("kraken siem: parser returned null"); return; } LogNormalizer normalizer = normalizers.get(fullName); if (normalizer == null) { slog.trace("kraken siem: normalizer not found for logger [{}]", fullName); return; } Map<String, Object> normalized = normalizer.normalize(parsed); if (normalized == null) { slog.debug("kraken siem: normalizer returned null"); return; } NormalizedLog normalizedLog = new NormalizedLog(ml.getOrgDomain(), normalized); String category = (String) normalizedLog.get("category"); if (category == null) { slog.debug("kraken siem: normalization category not found"); return; } CopyOnWriteArrayList<NormalizedLogListener> callbacks = normalizedLogCallbacks.get(category); if (callbacks == null) return; for (NormalizedLogListener callback : callbacks) { try { if (slog.isTraceEnabled()) slog.trace("kraken siem: normalized log [{}]", normalizedLog); callback.onLog(normalizedLog); } catch (Exception e) { slog.warn("kraken siem: normalized log listener callback should not throw any exception", e); } } } private Properties getTableMetadata(String tableName) { Set<String> keys = logTableRegistry.getTableMetadataKeys(tableName); Properties p = new Properties(); for (String key : keys) p.put(key, logTableRegistry.getTableMetadata(tableName, key)); return p; } private org.krakenapps.logstorage.Log convert(String fullName, Log log) { return new org.krakenapps.logstorage.Log(fullName, log.getDate(), log.getParams()); } // // LoggerRegistryEventListener 1 // @Override public void loggerAdded(Logger logger) { connectManagedLogger(logger); } @Override public void loggerRemoved(Logger logger) { disconnectManagedLogger(logger); } private void connectManagedLogger(Logger logger) { // check if it is managed loggers String fullName = logger.getFullName(); ManagedLogger ml = getManagedLogger(fullName); if (ml == null) return; // connect log pipe managedLoggers.put(fullName, ml); logger.addLogPipe(this); // create log parser Properties config = getTableMetadata(fullName); String parserFactoryName = config.getProperty("logparser"); if (parserFactoryName == null) { slog.debug("kraken siem: parser name [{}] not found for logger [{}]", parserFactoryName, fullName); return; } LogParserFactory parserFactory = parserFactoryRegistry.get(parserFactoryName); if (parserFactory == null) { slog.trace("kraken siem: parser factory [{}] not found for logger [{}]", parserFactoryName, fullName); return; } LogParser parser = parserFactory.createParser(config); parsers.put(fullName, parser); // create log normalizer String normalizerName = config.getProperty("normalizer"); if (normalizerName == null) { slog.debug("kraken siem: normalizer name [{}] not found for logger [{}]", normalizerName, fullName); return; } LogNormalizerFactory normalizerFactory = normalizerFactoryRegistry.get(normalizerName); if (normalizerFactory == null) { slog.trace("kraken siem: normalizer factory [{}] not found for logger [{}]", normalizerName, fullName); return; } LogNormalizer normalizer = normalizerFactory.createNormalizer(config); normalizers.put(fullName, normalizer); } private void disconnectManagedLogger(Logger logger) { String fullName = logger.getFullName(); managedLoggers.remove(fullName); logger.removeLogPipe(this); normalizers.remove(fullName); parsers.remove(fullName); } private ConfigCollection getCol() { ConfigDatabase db = configManager.getDatabase(); ConfigCollection col = db.ensureCollection("managed_logger"); return col; } }