diff --git a/.gitignore b/.gitignore index b1394fb..b4a2761 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ deploy.sh data/ /.local-execution-hints.log /.local-*-execution-hints.log +.idea/ +*.iml diff --git a/.travis.yml b/.travis.yml index 39fa021..4cbae5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ env: - ES_VERSION=1.5.0 - ES_VERSION=1.5.1 - ES_VERSION=1.5.2 + - ES_VERSION=1.7.0 cache: directories: - $HOME/.m2 diff --git a/README.md b/README.md index 9a1158b..0ab9e5f 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ There is no way to configure this on a per index basis. | Http Basic Plugin | elasticsearch | |-----------------------------|-----------------------| -| v1.5.0(master) | 1.5.x | +| v1.7.0(master) | 1.7.0 | +| v1.5.0 | 1.5.x | | v1.4.0 | 1.4.0 | | v1.3.0 | 1.3.0 | | v1.2.0 | 1.2.0 | diff --git a/pom.xml b/pom.xml index 12cc3dd..b59860b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,11 +1,11 @@ 4.0.0 com.asquera.elasticsearch elasticsearch-http-basic - 1.5.0 + 1.7.0 jar Elasticsearch Http Basic plugin Adds HTTP Basic authentication (BA) to your Elasticsearch cluster @@ -15,7 +15,7 @@ org.elasticsearch elasticsearch-parent - 1.5.0 + 1.7.0 @@ -88,11 +88,5 @@ - - - oss-snapshots - Sonatype OSS Snapshots - https://oss.sonatype.org/content/repositories/snapshots/ - - + diff --git a/src/main/java/com/asquera/elasticsearch/plugins/http/HttpBasicServer.java b/src/main/java/com/asquera/elasticsearch/plugins/http/HttpBasicServer.java index 2590060..ccee9b2 100644 --- a/src/main/java/com/asquera/elasticsearch/plugins/http/HttpBasicServer.java +++ b/src/main/java/com/asquera/elasticsearch/plugins/http/HttpBasicServer.java @@ -36,185 +36,181 @@ // # OR use beforeSend in $.ajax({ // http.cors.allow-headers: "X-Requested-With, Content-Type, Content-Length, Authorization" // + /** * @author Florian Gilcher (florian.gilcher@asquera.de) * @author Peter Karich */ public class HttpBasicServer extends HttpServer { - private final String user; - private final String password; - private final InetAddressWhitelist whitelist; - private final ProxyChains proxyChains; - private final String xForwardHeader; - private final boolean log; - - @Inject public HttpBasicServer(Settings settings, Environment environment, HttpServerTransport transport, - RestController restController, - NodeService nodeService) { - super(settings, environment, transport, restController, nodeService); - - this.user = settings.get("http.basic.user", "admin"); - this.password = settings.get("http.basic.password", "admin_pw"); - final boolean whitelistEnabled = settings.getAsBoolean("http.basic.ipwhitelist", true); - String [] whitelisted = new String[0]; - if (whitelistEnabled) { - whitelisted = settings.getAsArray("http.basic.ipwhitelist", new String[]{"localhost", "127.0.0.1"}); - } - this.whitelist = new InetAddressWhitelist(whitelisted); - this.proxyChains = new ProxyChains( - settings.getAsArray( - "http.basic.trusted_proxy_chains", new String[]{""})); - - // for AWS load balancers it is X-Forwarded-For -> hmmh does not work - this.xForwardHeader = settings.get("http.basic.xforward", ""); - this.log = settings.getAsBoolean("http.basic.log", true); - Loggers.getLogger(getClass()).info("using {}:{} with whitelist: {}, xforward header field: {}, trusted proxy chain: {}", - user, password, whitelist, xForwardHeader, proxyChains); - } - - @Override - public void internalDispatchRequest(final HttpRequest request, final HttpChannel channel) { - if (log) { - logRequest(request); - } - - if (authorized(request)) { - super.internalDispatchRequest(request, channel); - } else if (healthCheck(request)) { // display custom health check page when unauthorized (do not display too much server info) - channel.sendResponse(new BytesRestResponse(OK, "{\"OK\":{}}")); - } else { - logUnAuthorizedRequest(request); - BytesRestResponse response = new BytesRestResponse(UNAUTHORIZED, "Authentication Required"); - response.addHeader("WWW-Authenticate", "Basic realm=\"Restricted\""); - channel.sendResponse(response); - } - } - - private boolean healthCheck(final HttpRequest request) { - String path = request.path(); - return (request.method() == RestRequest.Method.GET) && path.equals("/"); - } - - /** - * - * - * @param request - * @return true if the request is authorized - */ - private boolean authorized(final HttpRequest request) { - return allowOptionsForCORS(request) || - authBasic(request) || ipAuthorized(request); - } - - /** - * - * - * @param request - * @return true iff the client is authorized by ip - */ - private boolean ipAuthorized(final HttpRequest request) { - boolean ipAuthorized = false; - String xForwardedFor = request.header(xForwardHeader); - Client client = new Client(getAddress(request), - whitelist, - new XForwardedFor(xForwardedFor), - proxyChains); - ipAuthorized = client.isAuthorized(); - if (ipAuthorized) { - if (log) { - String template = "Ip Authorized client: {}"; - Loggers.getLogger(getClass()).info(template, client); - } - } else { - String template = "Ip Unauthorized client: {}"; - Loggers.getLogger(getClass()).error(template, client); - } - return ipAuthorized; - } - - public String getDecoded(HttpRequest request) { - String authHeader = request.header("Authorization"); - if (authHeader == null) - return ""; - - String[] split = authHeader.split(" ", 2); - if (split.length != 2 || !split[0].equals("Basic")) - return ""; - try { - return new String(Base64.decode(split[1])); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - - private boolean authBasic(final HttpRequest request) { - String decoded = ""; - try { - decoded = getDecoded(request); - if (!decoded.isEmpty()) { - String[] userAndPassword = decoded.split(":", 2); - String givenUser = userAndPassword[0]; - String givenPass = userAndPassword[1]; - if (this.user.equals(givenUser) && this.password.equals(givenPass)) - return true; - } - } catch (Exception e) { - logger.warn("Retrieving of user and password failed for " + decoded + " ," + e.getMessage()); - } - return false; - } - - - /** - * - * - * @param request - * @return the IP adress of the direct client - */ - private InetAddress getAddress(HttpRequest request) { - return ((InetSocketAddress) request.getRemoteAddress()).getAddress(); - } - - - /** - * https://en.wikipedia.org/wiki/Cross-origin_resource_sharing the - * specification mandates that browsers “preflight” the request, soliciting - * supported methods from the server with an HTTP OPTIONS request - */ - private boolean allowOptionsForCORS(HttpRequest request) { - // in elasticsearch.yml set - // http.cors.allow-headers: "X-Requested-With, Content-Type, Content-Length, Authorization" - if (request.method() == Method.OPTIONS) { + private final String user; + private final String password; + private final InetAddressWhitelist whitelist; + private final ProxyChains proxyChains; + private final String xForwardHeader; + private final boolean log; + + @Inject + public HttpBasicServer(Settings settings, Environment environment, HttpServerTransport transport, + RestController restController, + NodeService nodeService) { + super(settings, environment, transport, restController, nodeService); + + this.user = settings.get("http.basic.user", "admin"); + this.password = settings.get("http.basic.password", "admin_pw"); + final boolean whitelistEnabled = settings.getAsBoolean("http.basic.ipwhitelist", true); + String[] whitelisted = new String[0]; + if (whitelistEnabled) { + whitelisted = settings.getAsArray("http.basic.ipwhitelist", new String[]{"localhost", "127.0.0.1"}); + } + this.whitelist = new InetAddressWhitelist(whitelisted); + this.proxyChains = new ProxyChains( + settings.getAsArray( + "http.basic.trusted_proxy_chains", new String[]{""})); + + // for AWS load balancers it is X-Forwarded-For -> hmmh does not work + this.xForwardHeader = settings.get("http.basic.xforward", ""); + this.log = settings.getAsBoolean("http.basic.log", true); + Loggers.getLogger(getClass()).info("using {}:{} with whitelist: {}, xforward header field: {}, trusted proxy chain: {}", + user, password, whitelist, xForwardHeader, proxyChains); + } + + @Override + public void internalDispatchRequest(final HttpRequest request, final HttpChannel channel) { + if (log) { + logRequest(request); + } + + if (authorized(request)) { + super.internalDispatchRequest(request, channel); + } else if (healthCheck(request)) { // display custom health check page when unauthorized (do not display too much server info) + channel.sendResponse(new BytesRestResponse(OK, "{\"OK\":{}}")); + } else { + logUnAuthorizedRequest(request); + BytesRestResponse response = new BytesRestResponse(UNAUTHORIZED, "Authentication Required"); + response.addHeader("WWW-Authenticate", "Basic realm=\"Restricted\""); + channel.sendResponse(response); + } + } + + private boolean healthCheck(final HttpRequest request) { + String path = request.path(); + return (request.method() == RestRequest.Method.GET) && path.equals("/"); + } + + /** + * @param request + * @return true if the request is authorized + */ + private boolean authorized(final HttpRequest request) { + return allowOptionsForCORS(request) || + authBasic(request) || ipAuthorized(request); + } + + /** + * @param request + * @return true iff the client is authorized by ip + */ + private boolean ipAuthorized(final HttpRequest request) { + boolean ipAuthorized = false; + String xForwardedFor = request.header(xForwardHeader); + Client client = new Client(getAddress(request), + whitelist, + new XForwardedFor(xForwardedFor), + proxyChains); + ipAuthorized = client.isAuthorized(); + if (ipAuthorized) { + if (log) { + String template = "Ip Authorized client: {}"; + Loggers.getLogger(getClass()).info(template, client); + } + } else { + String template = "Ip Unauthorized client: {}"; + Loggers.getLogger(getClass()).error(template, client); + } + return ipAuthorized; + } + + public String getDecoded(HttpRequest request) { + String authHeader = request.header("Authorization"); + if (authHeader == null) + return ""; + + String[] split = authHeader.split(" ", 2); + if (split.length != 2 || !split[0].equals("Basic")) + return ""; + try { + return new String(Base64.decode(split[1])); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + private boolean authBasic(final HttpRequest request) { + String decoded = ""; + try { + decoded = getDecoded(request); + if (!decoded.isEmpty()) { + String[] userAndPassword = decoded.split(":", 2); + String givenUser = userAndPassword[0]; + String givenPass = userAndPassword[1]; + if (this.user.equals(givenUser) && this.password.equals(givenPass)) + return true; + } + } catch (Exception e) { + logger.warn("Retrieving of user and password failed for " + decoded + " ," + e.getMessage()); + } + return false; + } + + + /** + * @param request + * @return the IP adress of the direct client + */ + private InetAddress getAddress(HttpRequest request) { + return ((InetSocketAddress) request.getRemoteAddress()).getAddress(); + } + + + /** + * https://en.wikipedia.org/wiki/Cross-origin_resource_sharing the + * specification mandates that browsers “preflight” the request, soliciting + * supported methods from the server with an HTTP OPTIONS request + */ + private boolean allowOptionsForCORS(HttpRequest request) { + // in elasticsearch.yml set + // http.cors.allow-headers: "X-Requested-With, Content-Type, Content-Length, Authorization" + if (request.method() == Method.OPTIONS) { // Loggers.getLogger(getClass()).error("CORS type {}, address {}, path {}, request {}, content {}", // request.method(), getAddress(request), request.path(), request.params(), request.content().toUtf8()); - return true; - } - return false; - } - - public void logRequest(final HttpRequest request) { - String addr = getAddress(request).getHostAddress(); - String t = "Authorization:{}, Host:{}, Path:{}, {}:{}, Request-IP:{}, " + - "Client-IP:{}, X-Client-IP{}"; - logger.info(t, - request.header("Authorization"), - request.header("Host"), - request.path(), - xForwardHeader, - request.header(xForwardHeader), - addr, - request.header("X-Client-IP"), - request.header("Client-IP")); - } - - public void logUnAuthorizedRequest(final HttpRequest request) { - String addr = getAddress(request).getHostAddress(); - String t = "UNAUTHORIZED type:{}, address:{}, path:{}, request:{}," - + "content:{}, credentials:{}"; - Loggers.getLogger(getClass()).error(t, - request.method(), addr, request.path(), request.params(), - request.content().toUtf8(), getDecoded(request)); - } + return true; + } + return false; + } + + public void logRequest(final HttpRequest request) { + String addr = getAddress(request).getHostAddress(); + String t = "Authorization:{}, Host:{}, Path:{}, {}:{}, Request-IP:{}, " + + "Client-IP:{}, X-Client-IP{}"; + logger.info(t, + request.header("Authorization"), + request.header("Host"), + request.path(), + xForwardHeader, + request.header(xForwardHeader), + addr, + request.header("X-Client-IP"), + request.header("Client-IP")); + } + + public void logUnAuthorizedRequest(final HttpRequest request) { + String addr = getAddress(request).getHostAddress(); + String t = "UNAUTHORIZED type:{}, address:{}, path:{}, request:{}," + + "content:{}, credentials:{}"; + Loggers.getLogger(getClass()).error(t, + request.method(), addr, request.path(), request.params(), + request.content().toUtf8(), getDecoded(request)); + } } diff --git a/src/main/java/com/asquera/elasticsearch/plugins/http/HttpBasicServerModule.java b/src/main/java/com/asquera/elasticsearch/plugins/http/HttpBasicServerModule.java index 229d4c7..8c1cd7c 100644 --- a/src/main/java/com/asquera/elasticsearch/plugins/http/HttpBasicServerModule.java +++ b/src/main/java/com/asquera/elasticsearch/plugins/http/HttpBasicServerModule.java @@ -1,20 +1,21 @@ package com.asquera.elasticsearch.plugins.http; -import org.elasticsearch.http.HttpServerModule; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.http.HttpServerModule; /** * @author Florian Gilcher (florian.gilcher@asquera.de) */ public class HttpBasicServerModule extends HttpServerModule { - public HttpBasicServerModule(Settings settings) { - super(settings); - } + public HttpBasicServerModule(Settings settings) { + super(settings); + } - @Override protected void configure() { - super.configure(); - bind(HttpBasicServer.class).asEagerSingleton(); - } + @Override + protected void configure() { + super.configure(); + bind(HttpBasicServer.class).asEagerSingleton(); + } } diff --git a/src/main/java/com/asquera/elasticsearch/plugins/http/HttpBasicServerPlugin.java b/src/main/java/com/asquera/elasticsearch/plugins/http/HttpBasicServerPlugin.java index 3e3ac6a..4671f73 100644 --- a/src/main/java/com/asquera/elasticsearch/plugins/http/HttpBasicServerPlugin.java +++ b/src/main/java/com/asquera/elasticsearch/plugins/http/HttpBasicServerPlugin.java @@ -1,59 +1,65 @@ package com.asquera.elasticsearch.plugins.http; +import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Module; +import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.AbstractPlugin; -import org.elasticsearch.common.component.LifecycleComponent; -import org.elasticsearch.common.settings.ImmutableSettings; import java.util.Collection; -import static org.elasticsearch.common.collect.Lists.*; +import static org.elasticsearch.common.collect.Lists.newArrayList; /** * @author Florian Gilcher (florian.gilcher@asquera.de) */ public class HttpBasicServerPlugin extends AbstractPlugin { - private boolean enabledByDefault = true; - private final Settings settings; - - @Inject public HttpBasicServerPlugin(Settings settings) { - this.settings = settings; - } - - @Override public String name() { - return "http-basic-server-plugin"; - } - - @Override public String description() { - return "HTTP Basic Server Plugin"; - } - - @Override public Collection> modules() { - Collection> modules = newArrayList(); - if (settings.getAsBoolean("http.basic.enabled", enabledByDefault)) { - modules.add(HttpBasicServerModule.class); - } - return modules; - } - - @Override public Collection> services() { - Collection> services = newArrayList(); - if (settings.getAsBoolean("http.basic.enabled", enabledByDefault)) { - services.add(HttpBasicServer.class); - } - return services; - } - - @Override public Settings additionalSettings() { - if (settings.getAsBoolean("http.basic.enabled", enabledByDefault)) { - return ImmutableSettings.settingsBuilder(). - put("http.enabled", false). - build(); - } else { - return ImmutableSettings.Builder.EMPTY_SETTINGS; - } - } + private boolean enabledByDefault = true; + private final Settings settings; + + @Inject + public HttpBasicServerPlugin(Settings settings) { + this.settings = settings; + } + + @Override + public String name() { + return "http-basic-server-plugin"; + } + + @Override + public String description() { + return "HTTP Basic Server Plugin"; + } + + @Override + public Collection> modules() { + Collection> modules = newArrayList(); + if (settings.getAsBoolean("http.basic.enabled", enabledByDefault)) { + modules.add(HttpBasicServerModule.class); + } + return modules; + } + + @Override + public Collection> services() { + Collection> services = newArrayList(); + if (settings.getAsBoolean("http.basic.enabled", enabledByDefault)) { + services.add(HttpBasicServer.class); + } + return services; + } + + @Override + public Settings additionalSettings() { + if (settings.getAsBoolean("http.basic.enabled", enabledByDefault)) { + return ImmutableSettings.settingsBuilder(). + put("http.enabled", false). + build(); + } else { + return ImmutableSettings.Builder.EMPTY_SETTINGS; + } + } } diff --git a/src/main/java/com/asquera/elasticsearch/plugins/http/auth/Client.java b/src/main/java/com/asquera/elasticsearch/plugins/http/auth/Client.java index 436ab33..1e99f66 100644 --- a/src/main/java/com/asquera/elasticsearch/plugins/http/auth/Client.java +++ b/src/main/java/com/asquera/elasticsearch/plugins/http/auth/Client.java @@ -1,7 +1,8 @@ package com.asquera.elasticsearch.plugins.http.auth; + import java.net.InetAddress; -import java.util.List; import java.util.ArrayList; +import java.util.List; /** * This class is responsible for determining the ip of the @@ -11,199 +12,192 @@ *

* a client can be trusted in this cases: *

    - *
  • client is directly connected to the server and its request ip is - * whitelisted - *
  • client is connected to the server via a chain of proxies trusted by - * the server, and its ip is whitelisted + *
  • client is directly connected to the server and its request ip is + * whitelisted + *
  • client is connected to the server via a chain of proxies trusted by + * the server, and its ip is whitelisted * * @author Ernesto Miguez (ernesto.miguez@asquera.de) */ public class Client { - private final InetAddress requestIp; - private final InetAddressWhitelist whitelist; - private final XForwardedFor xForwardedFor; - private final ProxyChains trustedProxyChains; - /** - * the trusted state of the client. A client is trusted if it is connected - * directly or if it is connected via a trusted ip chain. - */ - private boolean trusted; - /** - * the whitelisted state of the client. - */ - private boolean whitelisted; - /** - * the authorize state of the client. - * true iff the client it is conected via a trusted proxy chain and - * its client ip is whitelisted: - */ - private boolean authorized; + private final InetAddress requestIp; + private final InetAddressWhitelist whitelist; + private final XForwardedFor xForwardedFor; + private final ProxyChains trustedProxyChains; + /** + * the trusted state of the client. A client is trusted if it is connected + * directly or if it is connected via a trusted ip chain. + */ + private boolean trusted; + /** + * the whitelisted state of the client. + */ + private boolean whitelisted; + /** + * the authorize state of the client. + * true iff the client it is conected via a trusted proxy chain and + * its client ip is whitelisted: + */ + private boolean authorized; - /** - * @param requestIp - * @param whitelist - * @param xForwardedFor - * @param trustedProxyChains - * @param authorized - * @param trusted - * @param whitelisted - */ - public Client(InetAddress requestIp, InetAddressWhitelist whitelist, - XForwardedFor xForwardedFor, ProxyChains trustedProxyChains) - { - this.requestIp = requestIp; - this.whitelist = whitelist; - this.xForwardedFor = xForwardedFor; - this.trustedProxyChains = trustedProxyChains; - trusted = checkTrusted(); - whitelisted = checkWhitelisted(); - authorized = trusted && whitelisted; - } + /** + * @param requestIp + * @param whitelist + * @param xForwardedFor + * @param trustedProxyChains + * @param authorized + * @param trusted + * @param whitelisted + */ + public Client(InetAddress requestIp, InetAddressWhitelist whitelist, + XForwardedFor xForwardedFor, ProxyChains trustedProxyChains) { + this.requestIp = requestIp; + this.whitelist = whitelist; + this.xForwardedFor = xForwardedFor; + this.trustedProxyChains = trustedProxyChains; + trusted = checkTrusted(); + whitelisted = checkWhitelisted(); + authorized = trusted && whitelisted; + } - /** - * - * @return the String representation of the client's ip - * from the point of view of the server - *

    - * this can be one of: - *

      - *
    • client ip from X-Forwarded-For - *
    • a proxy ip in the proxy chain defined in X-Forwarded-For - *
    • the request ip - * - * - */ - public String ip() { - String ip = requestIp.getHostAddress(); - if (xForwardedFor.isSet()) { - ip = remoteClientIp(); - } - return ip; - } + /** + * @return the String representation of the client's ip + * from the point of view of the server + *

      + * this can be one of: + *

        + *
      • client ip from X-Forwarded-For + *
      • a proxy ip in the proxy chain defined in X-Forwarded-For + *
      • the request ip + */ + public String ip() { + String ip = requestIp.getHostAddress(); + if (xForwardedFor.isSet()) { + ip = remoteClientIp(); + } + return ip; + } - /** - * - * determines the trust state of the client. - *

        - * The client is trusted when: - *

          - *
        • it is not connected via proxy - *
        • it is connected via proxies and least one of the proxies subchains is trusted - * - * @return true if the client's proxy chain is trusted or if the client is - * not connected via proxy, false otherwise. - */ + /** + * determines the trust state of the client. + *

          + * The client is trusted when: + *

            + *
          • it is not connected via proxy + *
          • it is connected via proxies and least one of the proxies subchains is trusted + * + * @return true if the client's proxy chain is trusted or if the client is + * not connected via proxy, false otherwise. + */ - private boolean checkTrusted() { - boolean trusted = true; - if (xForwardedFor.isSet()) { - trusted = trustedProxyChains.trusts(requestChain()); - } - return trusted; - } + private boolean checkTrusted() { + boolean trusted = true; + if (xForwardedFor.isSet()) { + trusted = trustedProxyChains.trusts(requestChain()); + } + return trusted; + } - /** - * If the client conects via proxy its ip can be any of the listed - * in the X-Forwarded-For field; the trusted subchain determines which is the - * client's remote ip, as the ip previous to the first item of the trusted - * subchain. - *

            - * If the client doesn't connect via proxy its ip is the request ip - * - * @return true iff clients ip is whitelisted - * - */ - private boolean checkWhitelisted() { - boolean whitelisted = false; - if (xForwardedFor.isSet()) { - whitelisted = whitelist.contains(remoteClientIp()); - } else { - whitelisted = whitelist.contains(requestIp); - } - return whitelisted; - } + /** + * If the client conects via proxy its ip can be any of the listed + * in the X-Forwarded-For field; the trusted subchain determines which is the + * client's remote ip, as the ip previous to the first item of the trusted + * subchain. + *

            + * If the client doesn't connect via proxy its ip is the request ip + * + * @return true iff clients ip is whitelisted + */ + private boolean checkWhitelisted() { + boolean whitelisted = false; + if (xForwardedFor.isSet()) { + whitelisted = whitelist.contains(remoteClientIp()); + } else { + whitelisted = whitelist.contains(requestIp); + } + return whitelisted; + } - /** - * @return the trusted ip chain or null if none - */ - private ProxyChain trustedChain() { - return trustedProxyChains.trustedSubchain(requestChain()); - } + /** + * @return the trusted ip chain or null if none + */ + private ProxyChain trustedChain() { + return trustedProxyChains.trustedSubchain(requestChain()); + } - /** - * @param request_ip - * @return an request chain in the form of [proxy-1, .., proxy-n, request] - */ - private ProxyChain requestChain() { - List ipsChain = new ArrayList(); - ipsChain.addAll(xForwardedFor.proxies()); - ipsChain.add(requestIp.getHostAddress()); - return new ProxyChain(ipsChain); - } + /** + * @param request_ip + * @return an request chain in the form of [proxy-1, .., proxy-n, request] + */ + private ProxyChain requestChain() { + List ipsChain = new ArrayList(); + ipsChain.addAll(xForwardedFor.proxies()); + ipsChain.add(requestIp.getHostAddress()); + return new ProxyChain(ipsChain); + } - /** - * - * In case of X-Forwarded-For set, the ip of the remote client relative - * to the server is defined as follows: - *

            - * by default the remote client ip is the first ip of the X-Forwarded-For. - * If there is a sub proxy chain in the X-Forwarded-For that is trusted, the - * client ip is the ip that precedes the starting of the trusted subchain.

            - * example:

            - * - * a X-Forwarded-For value "1.1.1.1,2.2.2.2,3.3.3.3" with "3.3.3.3" as - * trusted proxy chain will have the "3.3.3.3" subchain trusted. This - * determines "2.2.2.2" as the server's remote client - * - * @return the remote client's ip relative to the server - */ - private String remoteClientIp() { - String clientIp = xForwardedFor.client(); - if (trustedChain() != null) { - List trustedProxies = trustedChain().getProxyChain(); - List proxies = xForwardedFor.proxies(); - proxies.removeAll(trustedProxies); - if (proxies.size() > 0) { - clientIp = proxies.get(proxies.size() - 1); - } - } - return clientIp; - } + /** + * In case of X-Forwarded-For set, the ip of the remote client relative + * to the server is defined as follows: + *

            + * by default the remote client ip is the first ip of the X-Forwarded-For. + * If there is a sub proxy chain in the X-Forwarded-For that is trusted, the + * client ip is the ip that precedes the starting of the trusted subchain.

            + * example:

            + *

            + * a X-Forwarded-For value "1.1.1.1,2.2.2.2,3.3.3.3" with "3.3.3.3" as + * trusted proxy chain will have the "3.3.3.3" subchain trusted. This + * determines "2.2.2.2" as the server's remote client + * + * @return the remote client's ip relative to the server + */ + private String remoteClientIp() { + String clientIp = xForwardedFor.client(); + if (trustedChain() != null) { + List trustedProxies = trustedChain().getProxyChain(); + List proxies = xForwardedFor.proxies(); + proxies.removeAll(trustedProxies); + if (proxies.size() > 0) { + clientIp = proxies.get(proxies.size() - 1); + } + } + return clientIp; + } - @Override - public String toString() { - String addr = requestIp.getHostAddress(); - String s = "client with request ip " + addr - + (xForwardedFor.isSet() ? ", remoteIp: " + remoteClientIp() : "") - + " is:" - + (authorized ? "Authorized" : "NotAuthorized") - + ", " - + (trusted ? "Trusted" : "NotTrusted") - + " (X-Forwarded-For: " - + xForwardedFor - + "), " - + (whitelisted ? "Whitelisted" : "NotWhitelisted"); - return s; - } + @Override + public String toString() { + String addr = requestIp.getHostAddress(); + String s = "client with request ip " + addr + + (xForwardedFor.isSet() ? ", remoteIp: " + remoteClientIp() : "") + + " is:" + + (authorized ? "Authorized" : "NotAuthorized") + + ", " + + (trusted ? "Trusted" : "NotTrusted") + + " (X-Forwarded-For: " + + xForwardedFor + + "), " + + (whitelisted ? "Whitelisted" : "NotWhitelisted"); + return s; + } - /** - * @return the trusted - */ - public boolean isTrusted() { - return trusted; - } + /** + * @return the trusted + */ + public boolean isTrusted() { + return trusted; + } - /** - * @return the whitelisted - */ - public boolean isWhitelisted() { - return whitelisted; - } + /** + * @return the whitelisted + */ + public boolean isWhitelisted() { + return whitelisted; + } - /** - * @return the authorized - */ - public boolean isAuthorized() { - return authorized; - } + /** + * @return the authorized + */ + public boolean isAuthorized() { + return authorized; + } } diff --git a/src/main/java/com/asquera/elasticsearch/plugins/http/auth/InetAddressWhitelist.java b/src/main/java/com/asquera/elasticsearch/plugins/http/auth/InetAddressWhitelist.java index dd416f2..6955a90 100644 --- a/src/main/java/com/asquera/elasticsearch/plugins/http/auth/InetAddressWhitelist.java +++ b/src/main/java/com/asquera/elasticsearch/plugins/http/auth/InetAddressWhitelist.java @@ -1,116 +1,103 @@ package com.asquera.elasticsearch.plugins.http.auth; + import org.elasticsearch.common.logging.Loggers; -import java.util.ArrayList; -import java.util.List; -import java.util.HashSet; -import java.util.Set; -import java.util.Iterator; -import java.util.Arrays; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.*; /** - * * Wraps the configured whitelisted ips. * It uses a set of {@link InetAddress} internally. *

            * - * - * * @author Ernesto Miguez (ernesto.miguez@asquera.de) */ public class InetAddressWhitelist { - private Set whitelist; - /** - * - * - * @param whitelist - */ - public InetAddressWhitelist(Set whitelist) { - this.whitelist = whitelist; - } + private Set whitelist; + + /** + * @param whitelist + */ + public InetAddressWhitelist(Set whitelist) { + this.whitelist = whitelist; + } - /** - * - * - * @param sWhitelist - * - */ - public InetAddressWhitelist(String[] sWhitelist) { - this(toInetAddress(Arrays.asList(sWhitelist))); - } + /** + * @param sWhitelist + */ + public InetAddressWhitelist(String[] sWhitelist) { + this(toInetAddress(Arrays.asList(sWhitelist))); + } - /** - * Checks the request ip for inclusion. - * Since that ip comes in a {@link InetAddress} representation, it is checked - * against the whitelist. - * - * @param candidate - * @return if the ip is included in the whitelist - */ - public Boolean contains(InetAddress candidate) { - return this.whitelist.contains(candidate); - } + /** + * Checks the request ip for inclusion. + * Since that ip comes in a {@link InetAddress} representation, it is checked + * against the whitelist. + * + * @param candidate + * @return if the ip is included in the whitelist + */ + public Boolean contains(InetAddress candidate) { + return this.whitelist.contains(candidate); + } - /** - * - * Checks the xForwardedFor defined client ip for inclusion. - * Since that ip comes in a String representation, it is checked against - * the String representation of the defined whitelist. - * - * @param candidate - * @return if the ip is included in the String representation of the - * whitelist ips - */ - public Boolean contains(String candidate) { - return getStringWhitelist().contains(candidate); - } + /** + * Checks the xForwardedFor defined client ip for inclusion. + * Since that ip comes in a String representation, it is checked against + * the String representation of the defined whitelist. + * + * @param candidate + * @return if the ip is included in the String representation of the + * whitelist ips + */ + public Boolean contains(String candidate) { + return getStringWhitelist().contains(candidate); + } - /** - * @return set of the string representations of the whitelist - */ - Set getStringWhitelist() { - Iterator iterator = this.whitelist.iterator(); - Set set = new HashSet(); - while (iterator.hasNext()) { - InetAddress next = iterator.next(); - set.add(next.getHostAddress()); - } - return set; - } + /** + * @return set of the string representations of the whitelist + */ + Set getStringWhitelist() { + Iterator iterator = this.whitelist.iterator(); + Set set = new HashSet(); + while (iterator.hasNext()) { + InetAddress next = iterator.next(); + set.add(next.getHostAddress()); + } + return set; + } - /** - * when an configured InetAddress is Unkown or Invalid it is dropped from the - * whitelist - * - * @param ips a list of string ips - * @return a list of {@link InetAddress} objects - * - */ - static Set toInetAddress(List ips) { - List listIps = new ArrayList(); - Iterator iterator = ips.iterator(); - while (iterator.hasNext()) { - String next = iterator.next(); - try { - listIps.add(InetAddress.getByName(next)); - } catch (UnknownHostException e) { - String template = "an ip set in the whitelist settings raised an " + - "UnknownHostException: {}, dropping it"; - Loggers.getLogger(InetAddressWhitelist.class).info(template, e.getMessage()); - } - } - return new HashSet(listIps); - } + /** + * when an configured InetAddress is Unkown or Invalid it is dropped from the + * whitelist + * + * @param ips a list of string ips + * @return a list of {@link InetAddress} objects + */ + static Set toInetAddress(List ips) { + List listIps = new ArrayList(); + Iterator iterator = ips.iterator(); + while (iterator.hasNext()) { + String next = iterator.next(); + try { + listIps.add(InetAddress.getByName(next)); + } catch (UnknownHostException e) { + String template = "an ip set in the whitelist settings raised an " + + "UnknownHostException: {}, dropping it"; + Loggers.getLogger(InetAddressWhitelist.class).info(template, e.getMessage()); + } + } + return new HashSet(listIps); + } - /** - * delegate method - */ - @Override - public String toString() { - return whitelist.toString(); - } + /** + * delegate method + */ + @Override + public String toString() { + return whitelist.toString(); + } } diff --git a/src/main/java/com/asquera/elasticsearch/plugins/http/auth/ProxyChain.java b/src/main/java/com/asquera/elasticsearch/plugins/http/auth/ProxyChain.java index 34ae434..1d29de9 100644 --- a/src/main/java/com/asquera/elasticsearch/plugins/http/auth/ProxyChain.java +++ b/src/main/java/com/asquera/elasticsearch/plugins/http/auth/ProxyChain.java @@ -3,102 +3,104 @@ import java.util.*; /** - * - * * This class wraps an ip chain (an ordered list of ips). * * @author Ernesto Miguez (ernesto.miguez@asquera.de) - * */ public class ProxyChain { - private List proxyChain; + private List proxyChain; - public ProxyChain() { - this.proxyChain = new ArrayList(); - } + public ProxyChain() { + this.proxyChain = new ArrayList(); + } - public ProxyChain(List proxyChain) { - this.proxyChain = proxyChain; - } + public ProxyChain(List proxyChain) { + this.proxyChain = proxyChain; + } - public ProxyChain(String proxyChain) { - this(new ArrayList(Arrays.asList(proxyChain.split(",")))); - } + public ProxyChain(String proxyChain) { + this(new ArrayList(Arrays.asList(proxyChain.split(",")))); + } - /** - * @return the proxy chain - */ - public List getProxyChain() { - return proxyChain; - } + /** + * @return the proxy chain + */ + public List getProxyChain() { + return proxyChain; + } - /** - * A subchain is every segment of the list matching by the tail, included - * itself.

            example: - * "1.1.1.1,2.2.2.2" is trusted by trusted list "3.3.3.3,4.4.4.4,2.2.2.2" since the - * subchain "2.2.2.2" is included in a subchain of the trusted list. - * - * @return a new {@link ProxyChain} instance having all the subchains of the - * present instance - */ - public ProxyChains subchains() { - List reversedIps = new ArrayList(proxyChain); - Collections.reverse(reversedIps); - ListIterator iterator = reversedIps.listIterator(); - ProxyChains subchains = new ProxyChains((Set)new HashSet()); - ProxyChain subChain = new ProxyChain(new ArrayList()); - while (iterator.hasNext()) { - String next = iterator.next(); - subChain.add(next); - List r = new ArrayList(subChain.getProxyChain()); - Collections.reverse(r); - subchains.add( new ProxyChain(r)); - } - return subchains; - } + /** + * A subchain is every segment of the list matching by the tail, included + * itself.

            example: + * "1.1.1.1,2.2.2.2" is trusted by trusted list "3.3.3.3,4.4.4.4,2.2.2.2" since the + * subchain "2.2.2.2" is included in a subchain of the trusted list. + * + * @return a new {@link ProxyChain} instance having all the subchains of the + * present instance + */ + public ProxyChains subchains() { + List reversedIps = new ArrayList(proxyChain); + Collections.reverse(reversedIps); + ListIterator iterator = reversedIps.listIterator(); + ProxyChains subchains = new ProxyChains((Set) new HashSet()); + ProxyChain subChain = new ProxyChain(new ArrayList()); + while (iterator.hasNext()) { + String next = iterator.next(); + subChain.add(next); + List r = new ArrayList(subChain.getProxyChain()); + Collections.reverse(r); + subchains.add(new ProxyChain(r)); + } + return subchains; + } - /** - * delegated method - * @param o - * @see List#add(Object o); - */ - public void add (Object o) { - proxyChain.add((String)o); - } + /** + * delegated method + * + * @param o + * @see List#add(Object o); + */ + public void add(Object o) { + proxyChain.add((String) o); + } - /** - * delegated method - * @see List#toString(); - */ - @Override - public String toString() { - return proxyChain.toString(); - } + /** + * delegated method + * + * @see List#toString(); + */ + @Override + public String toString() { + return proxyChain.toString(); + } - /** - * delegated method - * @see List#equals(); - */ - @Override - public boolean equals(Object c) { - return proxyChain.equals(((ProxyChain)c).getProxyChain()); - } + /** + * delegated method + * + * @see List#equals(); + */ + @Override + public boolean equals(Object c) { + return proxyChain.equals(((ProxyChain) c).getProxyChain()); + } - /** - * delegated method - * @see List#hashCode(); - */ - @Override - public int hashCode() { - return proxyChain.hashCode(); - } + /** + * delegated method + * + * @see List#hashCode(); + */ + @Override + public int hashCode() { + return proxyChain.hashCode(); + } - /** - * delegated method - * @see List#size(); - */ - public int size() { - return proxyChain.size(); - } + /** + * delegated method + * + * @see List#size(); + */ + public int size() { + return proxyChain.size(); + } } diff --git a/src/main/java/com/asquera/elasticsearch/plugins/http/auth/ProxyChains.java b/src/main/java/com/asquera/elasticsearch/plugins/http/auth/ProxyChains.java index 5a23363..58c19da 100644 --- a/src/main/java/com/asquera/elasticsearch/plugins/http/auth/ProxyChains.java +++ b/src/main/java/com/asquera/elasticsearch/plugins/http/auth/ProxyChains.java @@ -3,142 +3,132 @@ import java.util.*; /** - * This class wraps a set of {@link ProxyChain} + * This class wraps a set of {@link ProxyChain} * * @author Ernesto Miguez (ernesto.miguez@asquera.de) **/ public class ProxyChains { - private Set proxyChains; - - public ProxyChains(Set proxyChains) { - this.proxyChains = proxyChains; - } - - public ProxyChains(String[] proxyChains) { - this(getProxies(proxyChains)); - } - - /** - * - * An ip chain is trusted iff any of it subchains is contained in - * any of the instance subchains - * - * @param candidate the ip list to check - * @return true iff the candidate is included - */ - public Boolean trusts(ProxyChain candidate) { - return trustedSubchain(candidate) != null; - } - - /** - * - * Find the trusted subchain if any.

            note: Any chain is included in its subchains - * - * @param candidate - * @return the trusted subchain or nil if none is trusted. - * If more than one is trusted, the longuest will be returned - */ - public ProxyChain trustedSubchain(ProxyChain candidate) { - Set sub = subchains(); - sub.retainAll(candidate.subchains().getProxyChains()); - ProxyChain trusted = null; - if (!sub.isEmpty()) { - trusted = Collections.max(sub, new InetAddressChainComparator()); - } - return trusted; - } - - /** - * a comparator that uses ip chain size - */ - class InetAddressChainComparator implements Comparator { - @Override - public int compare(ProxyChain a, ProxyChain b) { - return a.size() < b.size() ? -1 : a.size() == b.size() ? 0 : 1; - } - } - - /** - * - * @return the set of subchains of the trusted ip proxy chains - * @see {@link ProxyChain#subchains()} - */ - - public Set subchains() { - Iterator iterator = proxyChains.iterator(); - Set set = new HashSet(); - while (iterator.hasNext()) { - ProxyChain next = iterator.next(); - set.addAll(next.subchains().getProxyChains()); - } - return set; - } - - /** - * - * delegated method - * - */ - public boolean contains(Object c) { - return proxyChains.contains(c); - } - - - /** - * - * delegated method - * - * @param chain - * @return if it is empty - */ - public boolean isEmpty() { - return proxyChains.isEmpty(); - } - - /** - * - * delegated method - * - * @param chain - * @return true if it could be added - */ - public boolean add(ProxyChain chain) { - return proxyChains.add(chain); - } - - - /** - * - * delegated method - */ - @Override - public String toString() { - return proxyChains.toString(); - } - - /** - * - * @param array of proxies represented as comma separated strings - * @return a {@link ProxyChain} object representing the passed proxies - * - */ - - private static Set getProxies(String[] ips) { - Set pChainSet = new HashSet(); - Iterator iterator = (Arrays.asList(ips)).iterator(); - while (iterator.hasNext()) { - String next = iterator.next(); - pChainSet.add(new ProxyChain(next)); - } - return pChainSet; - } - - /** - * @return the proxyChains - */ - public Set getProxyChains() { - return proxyChains; - } + private Set proxyChains; + + public ProxyChains(Set proxyChains) { + this.proxyChains = proxyChains; + } + + public ProxyChains(String[] proxyChains) { + this(getProxies(proxyChains)); + } + + /** + * An ip chain is trusted iff any of it subchains is contained in + * any of the instance subchains + * + * @param candidate the ip list to check + * @return true iff the candidate is included + */ + public Boolean trusts(ProxyChain candidate) { + return trustedSubchain(candidate) != null; + } + + /** + * Find the trusted subchain if any.

            note: Any chain is included in its subchains + * + * @param candidate + * @return the trusted subchain or nil if none is trusted. + * If more than one is trusted, the longuest will be returned + */ + public ProxyChain trustedSubchain(ProxyChain candidate) { + Set sub = subchains(); + sub.retainAll(candidate.subchains().getProxyChains()); + ProxyChain trusted = null; + if (!sub.isEmpty()) { + trusted = Collections.max(sub, new InetAddressChainComparator()); + } + return trusted; + } + + /** + * a comparator that uses ip chain size + */ + class InetAddressChainComparator implements Comparator { + @Override + public int compare(ProxyChain a, ProxyChain b) { + return a.size() < b.size() ? -1 : a.size() == b.size() ? 0 : 1; + } + } + + /** + * @return the set of subchains of the trusted ip proxy chains + * @see {@link ProxyChain#subchains()} + */ + + public Set subchains() { + Iterator iterator = proxyChains.iterator(); + Set set = new HashSet(); + while (iterator.hasNext()) { + ProxyChain next = iterator.next(); + set.addAll(next.subchains().getProxyChains()); + } + return set; + } + + /** + * delegated method + */ + public boolean contains(Object c) { + return proxyChains.contains(c); + } + + + /** + * delegated method + * + * @param chain + * @return if it is empty + */ + public boolean isEmpty() { + return proxyChains.isEmpty(); + } + + /** + * delegated method + * + * @param chain + * @return true if it could be added + */ + public boolean add(ProxyChain chain) { + return proxyChains.add(chain); + } + + + /** + * delegated method + */ + @Override + public String toString() { + return proxyChains.toString(); + } + + /** + * @param array of proxies represented as comma separated strings + * @return a {@link ProxyChain} object representing the passed proxies + */ + + private static Set getProxies(String[] ips) { + Set pChainSet = new HashSet(); + Iterator iterator = (Arrays.asList(ips)).iterator(); + while (iterator.hasNext()) { + String next = iterator.next(); + pChainSet.add(new ProxyChain(next)); + } + return pChainSet; + } + + /** + * @return the proxyChains + */ + public Set getProxyChains() { + return proxyChains; + } } diff --git a/src/main/java/com/asquera/elasticsearch/plugins/http/auth/XForwardedFor.java b/src/main/java/com/asquera/elasticsearch/plugins/http/auth/XForwardedFor.java index 16a4b4e..915597c 100644 --- a/src/main/java/com/asquera/elasticsearch/plugins/http/auth/XForwardedFor.java +++ b/src/main/java/com/asquera/elasticsearch/plugins/http/auth/XForwardedFor.java @@ -1,11 +1,10 @@ package com.asquera.elasticsearch.plugins.http.auth; -import java.util.List; -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; /** - * * Class that handles the values obtained from the X-Forwarded-For (XFF) HTTP Header * field. *

            @@ -18,75 +17,69 @@ * knowledge of which proxies are trustworthy, for instance by looking them * up in a whitelist of servers whose maintainers can be trusted. * - * @see X-Forwarded-For - * - * - * * @author Ernesto Miguez (ernesto.miguez@asquera.de) + * @see X-Forwarded-For */ public class XForwardedFor { - /** - * - * The X-Forwarded-For Header value as received in the request - * The general format of the field is: - * - * X-Forwarded-For: client, proxy1, proxy2 - */ - private final String xForwardedFor; + /** + * The X-Forwarded-For Header value as received in the request + * The general format of the field is: + *

            + * X-Forwarded-For: client, proxy1, proxy2 + */ + private final String xForwardedFor; - /** - * - * @param xForwardedFor - */ - public XForwardedFor(String xForwardedFor) { - this.xForwardedFor = xForwardedFor != null ? xForwardedFor : ""; - } + /** + * @param xForwardedFor + */ + public XForwardedFor(String xForwardedFor) { + this.xForwardedFor = xForwardedFor != null ? xForwardedFor : ""; + } - /** - * @return the ip of the client as defined by the X-Forwarded-For Header - */ - public String client() { - ArrayList splitted_ips = new ArrayList( - Arrays.asList(xForwardedFor.split(","))); - return splitted_ips.remove(0); - } + /** + * @return the ip of the client as defined by the X-Forwarded-For Header + */ + public String client() { + ArrayList splitted_ips = new ArrayList( + Arrays.asList(xForwardedFor.split(","))); + return splitted_ips.remove(0); + } - /** - * - * @return true if the X-Forwarded-For header was set - */ - public boolean isSet() { - return ! xForwardedFor.equals(""); - } + /** + * @return true if the X-Forwarded-For header was set + */ + public boolean isSet() { + return !xForwardedFor.equals(""); + } - /** - * delegate method - */ - @Override - public String toString() { - String s = "not used"; - if (isSet()) { - s = xForwardedFor; - } - return s; - } + /** + * delegate method + */ + @Override + public String toString() { + String s = "not used"; + if (isSet()) { + s = xForwardedFor; + } + return s; + } - /** - * @return the ips of the proxies between the client(as defined by the - * X-Forwarded-For Header) and the * server - */ - protected List proxies() { - ArrayList splitted_ips = new ArrayList( - Arrays.asList(xForwardedFor.split(","))); - splitted_ips.remove(0); - return splitted_ips; - } + /** + * @return the ips of the proxies between the client(as defined by the + * X-Forwarded-For Header) and the * server + */ + protected List proxies() { + ArrayList splitted_ips = new ArrayList( + Arrays.asList(xForwardedFor.split(","))); + splitted_ips.remove(0); + return splitted_ips; + } - /** - * @return the xForwardedFor - */ - public String getxForwardedFor() { - return xForwardedFor; - } + /** + * @return the xForwardedFor + */ + public String getxForwardedFor() { + return xForwardedFor; + } } diff --git a/src/test/java/com/asquera/elasticsearch/plugins/http/auth/integration/HttpBasicServerPluginIntegrationTest.java b/src/test/java/com/asquera/elasticsearch/plugins/http/auth/integration/HttpBasicServerPluginIntegrationTest.java index eea7066..64fbaf9 100644 --- a/src/test/java/com/asquera/elasticsearch/plugins/http/auth/integration/HttpBasicServerPluginIntegrationTest.java +++ b/src/test/java/com/asquera/elasticsearch/plugins/http/auth/integration/HttpBasicServerPluginIntegrationTest.java @@ -25,7 +25,7 @@ public class HttpBasicServerPluginIntegrationTest extends protected final String localhost = "127.0.0.1"; - public static HttpRequestBuilder httpClient() { + public static HttpRequestBuilder buildHttpClient() { HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class); InetSocketAddress address = ((InetSocketTransportAddress) httpServerTransport.boundAddress().publishAddress()).address(); return new HttpRequestBuilder(HttpClients.createDefault()).host(address.getHostName()).port(address.getPort()); @@ -42,7 +42,7 @@ protected Builder builderWithPlugin() { } protected HttpRequestBuilder requestWithCredentials(String credentials) throws Exception { - return httpClient().path("/_status") + return buildHttpClient().path("/_status") .addHeader("Authorization", "Basic " + Base64.encodeBytes(credentials.getBytes())); }