/*
*
* Copyright (C) 2012-2014 R T Huitema. All Rights Reserved.
* Web: www.42.co.nz
* Email: robert@42.co.nz
* Author: R T Huitema
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* 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 nz.co.fortytwo.signalk.processor;
import static nz.co.fortytwo.signalk.util.SignalKConstants.CONFIG_ACTION;
import static nz.co.fortytwo.signalk.util.SignalKConstants.CONFIG_ACTION_SAVE;
import static nz.co.fortytwo.signalk.util.SignalKConstants.CONTEXT;
import static nz.co.fortytwo.signalk.util.SignalKConstants.DEMO;
import static nz.co.fortytwo.signalk.util.SignalKConstants.INTERNAL_IP;
import static nz.co.fortytwo.signalk.util.SignalKConstants.MSG_SRC_IP;
import static nz.co.fortytwo.signalk.util.SignalKConstants.MSG_TYPE;
import static nz.co.fortytwo.signalk.util.SignalKConstants.PUT;
import static nz.co.fortytwo.signalk.util.SignalKConstants.SERIAL;
import static nz.co.fortytwo.signalk.util.SignalKConstants.UPDATES;
import static nz.co.fortytwo.signalk.util.SignalKConstants.self;
import java.util.ArrayList;
import java.util.List;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import mjson.Json;
import nz.co.fortytwo.signalk.util.ConfigConstants;
import nz.co.fortytwo.signalk.util.Util;
/**
* Parse the signalkModel json and remove anything that violates security
*
* @author robert
*
*/
public class IncomingSecurityFirewall extends SignalkProcessor implements
Processor {
private static Logger logger = LogManager
.getLogger(IncomingSecurityFirewall.class);
private List<String> whiteList = new ArrayList<String>();
private List<String> configAcceptList = new ArrayList<String>();
private List<String> denyList = new ArrayList<String>();
public IncomingSecurityFirewall() {
// load lists now
Json deny = Util.getConfigJsonArray(ConfigConstants.SECURITY_DENY);
Json white = Util.getConfigJsonArray(ConfigConstants.SECURITY_WHITE);
Json config = Util.getConfigJsonArray(ConfigConstants.SECURITY_CONFIG);
if (deny != null) {
for (Object o : deny.asList()) {
denyList.add((String) o);
}
}
if (white != null) {
for (Object o : white.asList()) {
whiteList.add((String) o);
}
}
if (config != null) {
for (Object o : config.asList()) {
configAcceptList.add((String) o);
}
}
}
public void process(Exchange exchange) throws Exception {
try {
// we trust local serial
String type = exchange.getIn().getHeader(MSG_TYPE,
String.class);
if (SERIAL.equals(type)||DEMO.equals(type)){
if (logger.isDebugEnabled())
logger.debug("Accepting msg:" + type);
return;
}
//TODO: check for valid XMPP/STOMP/MQTT SRC_IP ??
// we filter on ip
String srcIp = exchange.getIn().getHeader(MSG_SRC_IP,
String.class);
if (logger.isDebugEnabled())
logger.debug("Checking src ip:" + srcIp);
if (srcIp == null) {
logger.debug("Src ip null:"+exchange.getIn().getHeaders());
//exchange.getIn().setBody(null);
return;
}
// denied - drop now
//loop and check
if(Util.inNetworkList(denyList, srcIp)){
if (logger.isDebugEnabled())
logger.warn("Message DENIED for src ip :" + srcIp);
exchange.getIn().setBody(null);
return;
}
// save config only from allowed ips
String configSave = exchange.getIn().getHeader(
CONFIG_ACTION, String.class);
if (CONFIG_ACTION_SAVE.equals(configSave)) {
// must be in the configAcceptlist!
if(Util.inNetworkList(configAcceptList, srcIp)){
if (logger.isDebugEnabled())
logger.debug("Config save allowed for src ip:" + srcIp);
return;
} else {
if (logger.isDebugEnabled())
logger.warn("Config save DENIED for src ip:" + srcIp);
exchange.getIn().setBody(null);
return;
}
}
// we trust INTERNAL_IP
if (INTERNAL_IP.equals(type)) {
if (logger.isDebugEnabled())
logger.debug("Message allowed for src ip (internal):"+ srcIp+":"+exchange.getIn().getHeaders());
return;
}
// we trust our whitelist
if(Util.inNetworkList(whiteList, srcIp)){
if (logger.isDebugEnabled())
logger.debug("WhiteList message allowed for src ip:" + srcIp);
return;
}
// now we look for anomalies
// new incoming, so flag for acceptance
// exchange.getIn().setHeader(MSG_APPROVAL,
// REQUIRED);
// filter for evil
Json node = exchange.getIn().getBody(Json.class);
if (node.at(UPDATES) != null
|| node.at(PUT) != null) {
// cant be an update or put for this vessel since its external
if (node.at(CONTEXT).asString()
.contains(self)) {
if (logger.isDebugEnabled())
logger.debug("Message DENIED for src ip (spoofing self):"
+ srcIp);
exchange.getIn().setBody(null);
return;
}
}
// filter(node);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
public void filter(Json node) {
// apply rules to this object
// recurse into object
for (Json n : node.asJsonMap().values()) {
filter(n);
}
}
}