Commit 458e7007 authored by Andre Freyssinet's avatar Andre Freyssinet

Adds filtering based on source IP address.

parent bcfdf87a
/* /*
* JORAM: Java(TM) Open Reliable Asynchronous Messaging * 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
...@@ -23,8 +23,11 @@ ...@@ -23,8 +23,11 @@
package org.objectweb.joram.tools.rest.admin; package org.objectweb.joram.tools.rest.admin;
import java.net.ConnectException; import java.net.ConnectException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Properties; import java.util.Properties;
import java.util.StringTokenizer;
import javax.jms.Connection; import javax.jms.Connection;
import javax.jms.ConnectionFactory; import javax.jms.ConnectionFactory;
...@@ -45,23 +48,31 @@ import org.osgi.framework.BundleContext; ...@@ -45,23 +48,31 @@ import org.osgi.framework.BundleContext;
import fr.dyade.aaa.common.Debug; import fr.dyade.aaa.common.Debug;
public class AdminHelper { 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_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_HOST_PROP = "rest.jndi.factory.host";
public static final String BUNDLE_JNDI_FACTORY_PORT_PROP = "rest.jndi.factory.port"; 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_ROOT = "rest.admin.root";
public static final String BUNDLE_REST_ADMIN_PASS = "rest.admin.password"; 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 static AdminHelper helper = null;
private InitialContext ictx; private InitialContext ictx;
private BundleContext bundleContext; private BundleContext bundleContext;
private Properties jndiProps; private Properties jndiProps;
private JoramAdmin joramAdmin; private JoramAdmin joramAdmin;
private Connection cnx; private Connection cnx;
private String restAdminRoot; private String restAdminRoot;
private String restAdminPass; private String restAdminPass;
private String restAdminIPAllowed;
private IPFilter ipfilter;
private AdminHelper() { } private AdminHelper() { }
static public AdminHelper getInstance() { static public AdminHelper getInstance() {
...@@ -84,6 +95,25 @@ public class AdminHelper { ...@@ -84,6 +95,25 @@ public class AdminHelper {
return restAdminPass; 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() { public boolean authenticationRequired() {
return restAdminRoot != null && !restAdminRoot.isEmpty() && return restAdminRoot != null && !restAdminRoot.isEmpty() &&
restAdminPass != null && !restAdminPass.isEmpty(); restAdminPass != null && !restAdminPass.isEmpty();
...@@ -117,13 +147,18 @@ public class AdminHelper { ...@@ -117,13 +147,18 @@ public class AdminHelper {
public JoramAdmin getJoramAdmin() { public JoramAdmin getJoramAdmin() {
return joramAdmin; return joramAdmin;
} }
public void init(BundleContext bundleContext) throws Exception { public void init(BundleContext bundleContext) throws Exception {
this.bundleContext = bundleContext; this.bundleContext = bundleContext;
restAdminRoot = bundleContext.getProperty(BUNDLE_REST_ADMIN_ROOT); restAdminRoot = bundleContext.getProperty(BUNDLE_REST_ADMIN_ROOT);
restAdminPass = bundleContext.getProperty(BUNDLE_REST_ADMIN_PASS); 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"; String name = "dlft-admin";
startJoramAdmin(name); startJoramAdmin(name);
...@@ -253,4 +288,5 @@ public class AdminHelper { ...@@ -253,4 +288,5 @@ public class AdminHelper {
public int getLocalServerId() throws ConnectException, AdminException { public int getLocalServerId() throws ConnectException, AdminException {
return joramAdmin.getLocalServerId(); return joramAdmin.getLocalServerId();
} }
} }
/* /*
* JORAM: Java(TM) Open Reliable Asynchronous Messaging * 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
...@@ -24,6 +24,7 @@ package org.objectweb.joram.tools.rest.admin; ...@@ -24,6 +24,7 @@ package org.objectweb.joram.tools.rest.admin;
import java.io.IOException; import java.io.IOException;
import java.net.ConnectException; import java.net.ConnectException;
import java.net.InetAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
import java.util.List; import java.util.List;
...@@ -64,6 +65,8 @@ import org.objectweb.util.monolog.api.Logger; ...@@ -64,6 +65,8 @@ import org.objectweb.util.monolog.api.Logger;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import javax.servlet.http.HttpServletRequest;
import fr.dyade.aaa.common.Debug; import fr.dyade.aaa.common.Debug;
@Path("/") @Path("/")
...@@ -287,7 +290,7 @@ public class AdminService implements ContainerRequestFilter { ...@@ -287,7 +290,7 @@ public class AdminService implements ContainerRequestFilter {
@GET @GET
@Path("/queue") @Path("/queue")
@Produces({MediaType.TEXT_PLAIN}) @Produces({MediaType.TEXT_PLAIN})
public String listQueue( public synchronized String listQueue(
@Context HttpHeaders headers, @Context HttpHeaders headers,
@QueryParam("server-id") int serverId, @QueryParam("server-id") int serverId,
@Context UriInfo uriInfo) throws ConnectException, AdminException { @Context UriInfo uriInfo) throws ConnectException, AdminException {
...@@ -403,7 +406,7 @@ public class AdminService implements ContainerRequestFilter { ...@@ -403,7 +406,7 @@ public class AdminService implements ContainerRequestFilter {
@GET @GET
@Path("/topic") @Path("/topic")
@Produces({MediaType.TEXT_PLAIN}) @Produces({MediaType.TEXT_PLAIN})
public String listTopic( public synchronized String listTopic(
@Context HttpHeaders headers, @Context HttpHeaders headers,
@QueryParam("server-id") int serverId, @QueryParam("server-id") int serverId,
@Context UriInfo uriInfo) throws ConnectException, AdminException { @Context UriInfo uriInfo) throws ConnectException, AdminException {
...@@ -729,17 +732,33 @@ public class AdminService implements ContainerRequestFilter { ...@@ -729,17 +732,33 @@ public class AdminService implements ContainerRequestFilter {
return builder.build(); return builder.build();
} }
@Override @Context
public void filter(ContainerRequestContext requestContext) private HttpServletRequest httpServletRequest;
throws IOException {
@Override
public void filter(ContainerRequestContext requestContext) 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.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 // request headers
final MultivaluedMap<String, String> headers = requestContext.getHeaders(); final MultivaluedMap<String, String> headers = requestContext.getHeaders();
// authorization header // authorization header
......
/*
* 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<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;
}
}
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