Commit 483b65de authored by Andre Freyssinet's avatar Andre Freyssinet

Adds filtering based on source IP address.

parent 458e7007
/*
* JORAM: Java(TM) Open Reliable Asynchronous Messaging
* Copyright (C) 2018 ScalAgent Distributed Technologies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
* Initial developer(s): ScalAgent Distributed Technologies
* Contributor(s):
*/
package org.objectweb.joram.tools.rest.jmx;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.StringTokenizer;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import fr.dyade.aaa.common.Debug;
public class IPFilter {
public static Logger logger = Debug.getLogger(IPFilter.class.getName());
class Authorized {
String addr;
int ip;
int mask;
public Authorized(String addr, int ip, int mask) {
this.addr = addr;
this.ip = ip;
this.mask = mask;
}
public String toString() {
return addr;
}
}
private ArrayList<Authorized> ipAlloweds = null;
/**
* Creates list of IP allowed list from declaration.
*/
public IPFilter(String IPAllowedList) {
if (IPAllowedList == null) return;
IPAllowedList = IPAllowedList.trim();
if (IPAllowedList.length() == 0) return;
ipAlloweds = new ArrayList<Authorized>();
StringTokenizer st = new StringTokenizer(IPAllowedList, ",");
while (st.hasMoreTokens()) {
String addr = st.nextToken().trim();
String[] parts = addr.split("/");
String ip = parts[0];
int prefix;
if (parts.length < 2) {
prefix = 0;
} else {
prefix = Integer.parseInt(parts[1]);
}
Inet4Address a = null;
try {
a = (Inet4Address) InetAddress.getByName(ip);
} catch (UnknownHostException e) { }
byte[] b = a.getAddress();
ipAlloweds.add(new Authorized(addr,
((b[0] & 0xFF) << 24) | ((b[1] & 0xFF) << 16) | ((b[2] & 0xFF) << 8) | ((b[3] & 0xFF) << 0),
~((1 << (32 - prefix)) - 1)));
}
}
/**
* Check if the addr is authorized (all local address is authorized).
*
* @param addr The ip address to check
* @return true if authorized
* @throws UnknownHostException
* @throws SocketException
*/
public boolean checkIpAllowed(String addr) {
if (logger.isLoggable(BasicLevel.DEBUG))
logger.log(BasicLevel.DEBUG, "IPFilter.checkIpAllowed address=" + addr + ", ipAlloweds=" + ipAlloweds);
if (ipAlloweds == null)
return true;
try {
Inet4Address a = (Inet4Address) InetAddress.getByName(addr);
if (a.isAnyLocalAddress() || a.isLoopbackAddress())
return true;
// Check if the address is defined on any interface
if(NetworkInterface.getByInetAddress(a) != null)
return true;
byte[] b = a.getAddress();
int ipInt = ((b[0] & 0xFF) << 24) | ((b[1] & 0xFF) << 16) | ((b[2] & 0xFF) << 8) | ((b[3] & 0xFF) << 0);
for (Authorized authorized : ipAlloweds) {
if ((authorized.ip & authorized.mask) == (ipInt & authorized.mask)) {
if (logger.isLoggable(BasicLevel.DEBUG))
logger.log(BasicLevel.DEBUG, "IPFilter.checkIpAllowed address " + addr + " authorized.");
return true;
}
}
} catch (Exception exc) {
logger.log(BasicLevel.WARN, "IPFilter.checkIpAllowed " + addr, exc);
}
if (logger.isLoggable(BasicLevel.INFO))
logger.log(BasicLevel.INFO, "IPFilter.checkIpAllowed " + addr + " failed.");
return false;
}
}
...@@ -22,20 +22,30 @@ ...@@ -22,20 +22,30 @@
*/ */
package org.objectweb.joram.tools.rest.jmx; package org.objectweb.joram.tools.rest.jmx;
import java.net.SocketException;
import java.net.UnknownHostException;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger; import org.objectweb.util.monolog.api.Logger;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
import fr.dyade.aaa.common.Debug; import fr.dyade.aaa.common.Debug;
public class JmxHelper { public class JmxHelper {
public static Logger logger = Debug.getLogger(JmxHelper.class.getName());
public static final String BUNDLE_REST_JMX_ROOT = "rest.jmx.root"; public static final String BUNDLE_REST_JMX_ROOT = "rest.jmx.root";
public static final String BUNDLE_REST_JMX_PASS = "rest.jmx.password"; public static final String BUNDLE_REST_JMX_PASS = "rest.jmx.password";
public static final String BUNDLE_REST_JMX_IP_ALLOWED = "rest.jmx.ipallowed";
public static Logger logger = Debug.getLogger(JmxHelper.class.getName()); // Singleton
private static JmxHelper helper = null; private static JmxHelper helper = null;
private String restJmxRoot; private String restJmxRoot;
private String restJmxPass; private String restJmxPass;
private String restJmxIPAllowed;
private IPFilter ipfilter;
private JmxHelper() { } private JmxHelper() { }
...@@ -59,6 +69,25 @@ public class JmxHelper { ...@@ -59,6 +69,25 @@ public class JmxHelper {
return restJmxPass; return restJmxPass;
} }
/**
* @return the restJmxIPAllowed
*/
public String getRestJmxIPAllowed() {
return restJmxIPAllowed;
}
/**
* Check if the addr is authorized (all local address is authorized).
*
* @param addr The ip address to check
* @return true if authorized
* @throws UnknownHostException
* @throws SocketException
*/
public boolean checkIPAddress(String addr) {
return ipfilter.checkIpAllowed(addr);
}
public boolean authenticationRequired() { public boolean authenticationRequired() {
return restJmxRoot != null && !restJmxRoot.isEmpty() && return restJmxRoot != null && !restJmxRoot.isEmpty() &&
restJmxPass != null && !restJmxPass.isEmpty(); restJmxPass != null && !restJmxPass.isEmpty();
...@@ -67,5 +96,10 @@ public class JmxHelper { ...@@ -67,5 +96,10 @@ public class JmxHelper {
public void init(BundleContext bundleContext) throws Exception { public void init(BundleContext bundleContext) throws Exception {
restJmxRoot = bundleContext.getProperty(BUNDLE_REST_JMX_ROOT); restJmxRoot = bundleContext.getProperty(BUNDLE_REST_JMX_ROOT);
restJmxPass = bundleContext.getProperty(BUNDLE_REST_JMX_PASS); restJmxPass = bundleContext.getProperty(BUNDLE_REST_JMX_PASS);
restJmxIPAllowed = bundleContext.getProperty(BUNDLE_REST_JMX_IP_ALLOWED);
if (logger.isLoggable(BasicLevel.INFO))
logger.log(BasicLevel.INFO, "IPFilter allowedList = " + restJmxIPAllowed);
ipfilter = new IPFilter(restJmxIPAllowed);
} }
} }
...@@ -66,16 +66,18 @@ import org.objectweb.util.monolog.api.Logger; ...@@ -66,16 +66,18 @@ import org.objectweb.util.monolog.api.Logger;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
import javax.servlet.http.HttpServletRequest;
import fr.dyade.aaa.common.Debug; import fr.dyade.aaa.common.Debug;
@Path("/") @Path("/")
@Singleton @Singleton
public class JmxRestService implements ContainerRequestFilter { public class JmxRestService implements ContainerRequestFilter {
public static Logger logger = Debug.getLogger(JmxRestService.class.getName());
private static final String AUTHORIZATION_PROPERTY = "Authorization"; private static final String AUTHORIZATION_PROPERTY = "Authorization";
private static final String AUTHENTICATION_SCHEME = "Basic"; private static final String AUTHENTICATION_SCHEME = "Basic";
public static Logger logger = Debug.getLogger(JmxRestService.class.getName());
static MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); static MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
private final JmxHelper helper = JmxHelper.getInstance(); private final JmxHelper helper = JmxHelper.getInstance();
...@@ -599,18 +601,33 @@ public class JmxRestService implements ContainerRequestFilter { ...@@ -599,18 +601,33 @@ public class JmxRestService implements ContainerRequestFilter {
} }
return null; return null;
} }
@Context
private HttpServletRequest httpServletRequest;
@Override @Override
public void filter(ContainerRequestContext requestContext) public void filter(ContainerRequestContext requestContext) throws IOException {
throws IOException {
if (!helper.authenticationRequired()) { if (!helper.authenticationRequired()) {
if (logger.isLoggable(BasicLevel.DEBUG)) if (logger.isLoggable(BasicLevel.INFO))
logger.log(BasicLevel.DEBUG, "no authentication."); logger.log(BasicLevel.INFO, "no authentication.");
// no authentication
return; return;
} }
if (httpServletRequest != null) {
// JSR-315/JSR-339 compliant server
String remoteIpAddress = httpServletRequest.getRemoteAddr();
if (remoteIpAddress != null) {
if (! helper.checkIPAddress(remoteIpAddress)) {
Response response = Response.status(Response.Status.UNAUTHORIZED)
.header("WWW-Authenticate", "Basic realm=\"executives\"")
.entity("You cannot access this resource (IP not allowed)").build();
requestContext.abortWith(response);
return;
}
}
if (logger.isLoggable(BasicLevel.DEBUG))
logger.log(BasicLevel.DEBUG, "request from: " + remoteIpAddress);
}
// request headers // request headers
final MultivaluedMap<String, String> headers = requestContext.getHeaders(); final MultivaluedMap<String, String> headers = requestContext.getHeaders();
// authorization header // authorization header
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment