From 458e7007f35d0f323ed2c2b5698c6408570e626a Mon Sep 17 00:00:00 2001 From: freyssin Date: Mon, 22 Oct 2018 17:03:05 +0200 Subject: [PATCH] Adds filtering based on source IP address. --- .../joram/tools/rest/admin/AdminHelper.java | 44 +++++- .../joram/tools/rest/admin/AdminService.java | 37 +++-- .../joram/tools/rest/admin/IPFilter.java | 132 ++++++++++++++++++ 3 files changed, 200 insertions(+), 13 deletions(-) create mode 100644 joram/joram/tools/rest/admin/src/main/java/org/objectweb/joram/tools/rest/admin/IPFilter.java diff --git a/joram/joram/tools/rest/admin/src/main/java/org/objectweb/joram/tools/rest/admin/AdminHelper.java b/joram/joram/tools/rest/admin/src/main/java/org/objectweb/joram/tools/rest/admin/AdminHelper.java index c6e6bb61e..2d631006d 100644 --- a/joram/joram/tools/rest/admin/src/main/java/org/objectweb/joram/tools/rest/admin/AdminHelper.java +++ b/joram/joram/tools/rest/admin/src/main/java/org/objectweb/joram/tools/rest/admin/AdminHelper.java @@ -1,6 +1,6 @@ /* * JORAM: Java(TM) Open Reliable Asynchronous Messaging - * Copyright (C) 2016 ScalAgent Distributed Technologies + * Copyright (C) 2016 - 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 @@ -23,8 +23,11 @@ package org.objectweb.joram.tools.rest.admin; import java.net.ConnectException; +import java.net.SocketException; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Properties; +import java.util.StringTokenizer; import javax.jms.Connection; import javax.jms.ConnectionFactory; @@ -45,23 +48,31 @@ import org.osgi.framework.BundleContext; import fr.dyade.aaa.common.Debug; public class AdminHelper { + public static Logger logger = Debug.getLogger(AdminHelper.class.getName()); public static final String BUNDLE_JNDI_FACTORY_INITIAL_PROP = "rest.jndi.factory.initial"; public static final String BUNDLE_JNDI_FACTORY_HOST_PROP = "rest.jndi.factory.host"; public static final String BUNDLE_JNDI_FACTORY_PORT_PROP = "rest.jndi.factory.port"; + public static final String BUNDLE_REST_ADMIN_ROOT = "rest.admin.root"; public static final String BUNDLE_REST_ADMIN_PASS = "rest.admin.password"; + public static final String BUNDLE_REST_ADMIN_IP_ALLOWED = "rest.admin.ipallowed"; - public static Logger logger = Debug.getLogger(AdminHelper.class.getName()); + // Singleton private static AdminHelper helper = null; + private InitialContext ictx; private BundleContext bundleContext; private Properties jndiProps; private JoramAdmin joramAdmin; private Connection cnx; + private String restAdminRoot; private String restAdminPass; - + + private String restAdminIPAllowed; + private IPFilter ipfilter; + private AdminHelper() { } static public AdminHelper getInstance() { @@ -84,6 +95,25 @@ public class AdminHelper { return restAdminPass; } + /** + * @return the restAdminIPAllowed + */ + public String getRestAdminIPAllowed() { + return restAdminIPAllowed; + } + + /** + * 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) { + return ipfilter.checkIpAllowed(addr); + } + public boolean authenticationRequired() { return restAdminRoot != null && !restAdminRoot.isEmpty() && restAdminPass != null && !restAdminPass.isEmpty(); @@ -117,13 +147,18 @@ public class AdminHelper { public JoramAdmin getJoramAdmin() { return joramAdmin; } - + public void init(BundleContext bundleContext) throws Exception { this.bundleContext = bundleContext; restAdminRoot = bundleContext.getProperty(BUNDLE_REST_ADMIN_ROOT); restAdminPass = bundleContext.getProperty(BUNDLE_REST_ADMIN_PASS); + restAdminIPAllowed = bundleContext.getProperty(BUNDLE_REST_ADMIN_IP_ALLOWED); + if (logger.isLoggable(BasicLevel.INFO)) + logger.log(BasicLevel.INFO, "IPFilter allowedList = " + restAdminIPAllowed); + ipfilter = new IPFilter(restAdminIPAllowed); + String name = "dlft-admin"; startJoramAdmin(name); @@ -253,4 +288,5 @@ public class AdminHelper { public int getLocalServerId() throws ConnectException, AdminException { return joramAdmin.getLocalServerId(); } + } diff --git a/joram/joram/tools/rest/admin/src/main/java/org/objectweb/joram/tools/rest/admin/AdminService.java b/joram/joram/tools/rest/admin/src/main/java/org/objectweb/joram/tools/rest/admin/AdminService.java index b78fb8587..6f8619fc4 100644 --- a/joram/joram/tools/rest/admin/src/main/java/org/objectweb/joram/tools/rest/admin/AdminService.java +++ b/joram/joram/tools/rest/admin/src/main/java/org/objectweb/joram/tools/rest/admin/AdminService.java @@ -1,6 +1,6 @@ /* * JORAM: Java(TM) Open Reliable Asynchronous Messaging - * Copyright (C) 2016 - 2017 ScalAgent Distributed Technologies + * Copyright (C) 2016 - 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 @@ -24,6 +24,7 @@ package org.objectweb.joram.tools.rest.admin; import java.io.IOException; import java.net.ConnectException; +import java.net.InetAddress; import java.util.ArrayList; import java.util.Base64; import java.util.List; @@ -64,6 +65,8 @@ import org.objectweb.util.monolog.api.Logger; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import javax.servlet.http.HttpServletRequest; + import fr.dyade.aaa.common.Debug; @Path("/") @@ -287,7 +290,7 @@ public class AdminService implements ContainerRequestFilter { @GET @Path("/queue") @Produces({MediaType.TEXT_PLAIN}) - public String listQueue( + public synchronized String listQueue( @Context HttpHeaders headers, @QueryParam("server-id") int serverId, @Context UriInfo uriInfo) throws ConnectException, AdminException { @@ -403,7 +406,7 @@ public class AdminService implements ContainerRequestFilter { @GET @Path("/topic") @Produces({MediaType.TEXT_PLAIN}) - public String listTopic( + public synchronized String listTopic( @Context HttpHeaders headers, @QueryParam("server-id") int serverId, @Context UriInfo uriInfo) throws ConnectException, AdminException { @@ -729,17 +732,33 @@ public class AdminService implements ContainerRequestFilter { return builder.build(); } - @Override - public void filter(ContainerRequestContext requestContext) - throws IOException { + @Context + private HttpServletRequest httpServletRequest; + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { if (!helper.authenticationRequired()) { - if (logger.isLoggable(BasicLevel.DEBUG)) - logger.log(BasicLevel.DEBUG, "no authentication."); - // no authentication + if (logger.isLoggable(BasicLevel.INFO)) + logger.log(BasicLevel.INFO, "no authentication."); return; } + if (httpServletRequest != null) { + // JSR-315/JSR-339 compliant server + String remoteIpAddress = httpServletRequest.getRemoteAddr(); + if (remoteIpAddress != null) { + if (! helper.checkIpAllowed(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 final MultivaluedMap headers = requestContext.getHeaders(); // authorization header diff --git a/joram/joram/tools/rest/admin/src/main/java/org/objectweb/joram/tools/rest/admin/IPFilter.java b/joram/joram/tools/rest/admin/src/main/java/org/objectweb/joram/tools/rest/admin/IPFilter.java new file mode 100644 index 000000000..ec87145a3 --- /dev/null +++ b/joram/joram/tools/rest/admin/src/main/java/org/objectweb/joram/tools/rest/admin/IPFilter.java @@ -0,0 +1,132 @@ +/* + * 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.admin; + +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 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(); + 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; + } +} -- 2.22.0