From a551c5544c9113b7e33d9dd3daf87f8eafe53c7e Mon Sep 17 00:00:00 2001 From: diuis Date: Thu, 27 Feb 2020 02:29:35 +0100 Subject: [PATCH 1/8] add starts with predicate for accepted hosts --- .../common/jaxrs/SecurityValidator.java | 8 +++- .../common/jaxrs/SecurityValidatorTest.java | 45 ++++++++++++++++++- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java index d7d364f..683d8ba 100644 --- a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java +++ b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java @@ -43,13 +43,16 @@ public class SecurityValidator { private List acceptedRoles; public void init() { + acceptedRoles = config("geronimo.metrics.jaxrs.acceptedRoles", identity()).orElse(null); acceptedHosts = config("geronimo.metrics.jaxrs.acceptedHosts", value -> { if ("".equals(value)) { return LOCAL_MATCHER; } - return (Predicate) value::equals; + return Optional.ofNullable(value) + .filter(v -> v.endsWith(".")) + .map(v -> ((Predicate) p -> p.startsWith(v))) + .orElse((Predicate) value::equals); }).orElse(singletonList(LOCAL_MATCHER)); - acceptedRoles = config("geronimo.metrics.jaxrs.acceptedRoles", identity()).orElse(null); } public void checkSecurity(final SecurityContext securityContext, final UriInfo uriInfo) { @@ -85,4 +88,5 @@ private Optional> config(final String key, final Function protected String config(final String key) { return System.getProperty(key); } + } diff --git a/geronimo-metrics-common/src/test/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidatorTest.java b/geronimo-metrics-common/src/test/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidatorTest.java index c2e0aef..ed650b7 100644 --- a/geronimo-metrics-common/src/test/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidatorTest.java +++ b/geronimo-metrics-common/src/test/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidatorTest.java @@ -27,8 +27,6 @@ import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; -import com.sun.org.apache.regexp.internal.RE; - import org.junit.Test; public class SecurityValidatorTest { @@ -96,6 +94,7 @@ public String getAuthenticationScheme() { } }; private static final UriInfo REMOTE = uri("http://geronimo.somewhere"); + private static final UriInfo REMOTE_WITH_DOT = uri("http://10.0.0.0"); private static final UriInfo LOCALHOST = uri("http://localhost"); @Test @@ -105,6 +104,20 @@ public void localValid() { }}.checkSecurity(ANONYMOUS, LOCALHOST); } + @Test + public void remoteWithDotValid() { + new SecurityValidator() { + { + init(); + } + + @Override + protected String config(final String key) { + return key.endsWith("acceptedHosts") ? "10." : null; + } + }.checkSecurity(ANONYMOUS, REMOTE_WITH_DOT); + } + @Test(expected = WebApplicationException.class) public void remoteInvalid() { new SecurityValidator() {{ @@ -168,6 +181,34 @@ protected String config(final String key) { }.checkSecurity(ADMIN, REMOTE); } + @Test + public void roleAndHostThatEndsWithDotValid() { + new SecurityValidator() { + { + init(); + } + + @Override + protected String config(final String key) { + return key.endsWith("acceptedRoles") ? "admin" : key.endsWith("acceptedHosts") ? "10." : null; + } + }.checkSecurity(ADMIN, REMOTE_WITH_DOT); + } + + @Test(expected = WebApplicationException.class) + public void roleAnonymousAndHostThatEndsWithDotValid() { + new SecurityValidator() { + { + init(); + } + + @Override + protected String config(final String key) { + return key.endsWith("acceptedRoles") ? "admin" : key.endsWith("acceptedHosts") ? "10." : null; + } + }.checkSecurity(LOGGED_NO_ROLE, REMOTE_WITH_DOT); + } + private static UriInfo uri(final String request) { return new UriInfoMock(request); } From f1a3609f2059a2a22de70a52568a8e99183517d4 Mon Sep 17 00:00:00 2001 From: diuis Date: Thu, 27 Feb 2020 09:02:17 +0100 Subject: [PATCH 2/8] add eclipse files and directories to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 218afde..15b39fb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ *.iml target src/test/java/io/* +.classpath +.project +.settings \ No newline at end of file From 9f739ec6727d48a66ba863834170090d3694475f Mon Sep 17 00:00:00 2001 From: diuis Date: Thu, 27 Feb 2020 09:06:56 +0100 Subject: [PATCH 3/8] Update SecurityValidator.java --- .../microprofile/metrics/common/jaxrs/SecurityValidator.java | 1 - 1 file changed, 1 deletion(-) diff --git a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java index 683d8ba..9d033b4 100644 --- a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java +++ b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java @@ -88,5 +88,4 @@ private Optional> config(final String key, final Function protected String config(final String key) { return System.getProperty(key); } - } From 840091aa2862a52659841d4db23eec14c7b97a9b Mon Sep 17 00:00:00 2001 From: diuis Date: Thu, 27 Feb 2020 09:15:47 +0100 Subject: [PATCH 4/8] fix typo (hosts instead of roles) --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index fc305ce..97b4a26 100644 --- a/README.adoc +++ b/README.adoc @@ -42,7 +42,7 @@ At least one must match to let the request pass, if none is set this validation Note that a request without a principal will lead to a HTTP 401 whereas a request with a principal but not the right role will issue a HTTP 403. The host validation will use the JAX-RS `UriInfo#getRequestUri`. -It relies on the system property `geronimo.metrics.jaxrs.acceptedHosts` and it takes a comma separated list of roles. +It relies on the system property `geronimo.metrics.jaxrs.acceptedHosts` and it takes a comma separated list of hosts. At least one must match to let the request pass, if none is set this validation is ignored. The `` value is an alias for `127.x.y.z` or `1::x` IP or `localhost`. From a814d9989e6e8b07fb96bf0b202ef8065855d4b5 Mon Sep 17 00:00:00 2001 From: diuis Date: Thu, 27 Feb 2020 09:22:01 +0100 Subject: [PATCH 5/8] add doc for the starts with match --- README.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index 97b4a26..50163d2 100644 --- a/README.adoc +++ b/README.adoc @@ -44,6 +44,7 @@ Note that a request without a principal will lead to a HTTP 401 whereas a reques The host validation will use the JAX-RS `UriInfo#getRequestUri`. It relies on the system property `geronimo.metrics.jaxrs.acceptedHosts` and it takes a comma separated list of hosts. At least one must match to let the request pass, if none is set this validation is ignored. +If the host value ends with a dot, the match is a start with match and not an exact match. The `` value is an alias for `127.x.y.z` or `1::x` IP or `localhost`. Configuration example: @@ -51,7 +52,7 @@ Configuration example: [source] ---- -Dgeronimo.metrics.jaxrs.acceptedRoles=ops \ --Dgeronimo.metrics.jaxrs.acceptedHosts=my.remote.host +-Dgeronimo.metrics.jaxrs.acceptedHosts=my.remote.host,10.0.0. ---- IMPORTANT: the default is `geronimo.metrics.jaxrs.acceptedHosts=` but you can disable the endpoints using `geronimo.metrics.jaxrs.activated=false`. From 31329198492fe544ac67485d6eb342d05a0c9048 Mon Sep 17 00:00:00 2001 From: diuis Date: Thu, 27 Feb 2020 14:58:39 +0100 Subject: [PATCH 6/8] add ip range for accepted hosts security validation --- .../common/jaxrs/SecurityValidator.java | 30 +++++++-- .../common/jaxrs/SecurityValidatorTest.java | 62 ++++++++++++++++--- 2 files changed, 78 insertions(+), 14 deletions(-) diff --git a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java index 9d033b4..b9a647a 100644 --- a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java +++ b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java @@ -16,11 +16,16 @@ */ package org.apache.geronimo.microprofile.metrics.common.jaxrs; +import static java.lang.Math.max; +import static java.lang.Math.min; import static java.util.Collections.singletonList; import static java.util.Optional.ofNullable; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toList; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.List; import java.util.Optional; import java.util.function.Function; @@ -43,16 +48,33 @@ public class SecurityValidator { private List acceptedRoles; public void init() { - acceptedRoles = config("geronimo.metrics.jaxrs.acceptedRoles", identity()).orElse(null); acceptedHosts = config("geronimo.metrics.jaxrs.acceptedHosts", value -> { if ("".equals(value)) { return LOCAL_MATCHER; } return Optional.ofNullable(value) - .filter(v -> v.endsWith(".")) - .map(v -> ((Predicate) p -> p.startsWith(v))) - .orElse((Predicate) value::equals); + .filter(range -> range.startsWith("[") && range.endsWith("]")) + .map(v -> ((Predicate) ipToValidate -> { + return Optional.of(value) + .map(range -> range.subSequence(1, range.length() - 1).toString()) + .map(rangeWithoutBraces -> rangeWithoutBraces.split("\\.\\.")) + .filter(values -> values.length == 2) + .map(rangeArray -> { + try { + long addressMin = new BigInteger(InetAddress.getByName(rangeArray[0]) + .getAddress()).longValue(); + long addressMax = new BigInteger(InetAddress.getByName(rangeArray[1]) + .getAddress()).longValue(); + long addressToValidate = new BigInteger(InetAddress.getByName(ipToValidate) + .getAddress()).longValue(); + return max(addressMin, addressToValidate) == min(addressToValidate, addressMax); + } catch (UnknownHostException e) { + return false; + } + }).orElse(false); + })).orElse((Predicate) value::equals); }).orElse(singletonList(LOCAL_MATCHER)); + acceptedRoles = config("geronimo.metrics.jaxrs.acceptedRoles", identity()).orElse(null); } public void checkSecurity(final SecurityContext securityContext, final UriInfo uriInfo) { diff --git a/geronimo-metrics-common/src/test/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidatorTest.java b/geronimo-metrics-common/src/test/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidatorTest.java index ed650b7..b0c1ffe 100644 --- a/geronimo-metrics-common/src/test/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidatorTest.java +++ b/geronimo-metrics-common/src/test/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidatorTest.java @@ -94,7 +94,7 @@ public String getAuthenticationScheme() { } }; private static final UriInfo REMOTE = uri("http://geronimo.somewhere"); - private static final UriInfo REMOTE_WITH_DOT = uri("http://10.0.0.0"); + private static final UriInfo REMOTE_IP = uri("http://10.10.10.1"); private static final UriInfo LOCALHOST = uri("http://localhost"); @Test @@ -105,7 +105,7 @@ public void localValid() { } @Test - public void remoteWithDotValid() { + public void remoteWithIpRangeValid() { new SecurityValidator() { { init(); @@ -113,9 +113,23 @@ public void remoteWithDotValid() { @Override protected String config(final String key) { - return key.endsWith("acceptedHosts") ? "10." : null; + return key.endsWith("acceptedHosts") ? "[10.10.10.0..10.10.10.255]" : null; } - }.checkSecurity(ANONYMOUS, REMOTE_WITH_DOT); + }.checkSecurity(ANONYMOUS, REMOTE_IP); + } + + @Test(expected = WebApplicationException.class) + public void remoteWithIpRangeInvalid() { + new SecurityValidator() { + { + init(); + } + + @Override + protected String config(final String key) { + return key.endsWith("acceptedHosts") ? "[20.10.10.0..20.10.10.255]" : null; + } + }.checkSecurity(ANONYMOUS, REMOTE_IP); } @Test(expected = WebApplicationException.class) @@ -182,7 +196,35 @@ protected String config(final String key) { } @Test - public void roleAndHostThatEndsWithDotValid() { + public void roleAndIpRangeValid() { + new SecurityValidator() { + { + init(); + } + + @Override + protected String config(final String key) { + return key.endsWith("acceptedRoles") ? "admin" : key.endsWith("acceptedHosts") ? "[10.10.10.0..10.10.10.255]" : null; + } + }.checkSecurity(ADMIN, REMOTE_IP); + } + + @Test(expected = WebApplicationException.class) + public void roleAndIpRangeInvalid() { + new SecurityValidator() { + { + init(); + } + + @Override + protected String config(final String key) { + return key.endsWith("acceptedRoles") ? "admin" : key.endsWith("acceptedHosts") ? "[20.10.10.0..20.10.10.255]" : null; + } + }.checkSecurity(ADMIN, REMOTE_IP); + } + + @Test(expected = WebApplicationException.class) + public void roleAnonymousAndIpRangeValid() { new SecurityValidator() { { init(); @@ -190,13 +232,13 @@ public void roleAndHostThatEndsWithDotValid() { @Override protected String config(final String key) { - return key.endsWith("acceptedRoles") ? "admin" : key.endsWith("acceptedHosts") ? "10." : null; + return key.endsWith("acceptedRoles") ? "admin" : key.endsWith("acceptedHosts") ? "[10.10.10.0..10.10.10.255]" : null; } - }.checkSecurity(ADMIN, REMOTE_WITH_DOT); + }.checkSecurity(LOGGED_NO_ROLE, REMOTE_IP); } @Test(expected = WebApplicationException.class) - public void roleAnonymousAndHostThatEndsWithDotValid() { + public void roleAnonymousAndIpRangeInvalid() { new SecurityValidator() { { init(); @@ -204,9 +246,9 @@ public void roleAnonymousAndHostThatEndsWithDotValid() { @Override protected String config(final String key) { - return key.endsWith("acceptedRoles") ? "admin" : key.endsWith("acceptedHosts") ? "10." : null; + return key.endsWith("acceptedRoles") ? "admin" : key.endsWith("acceptedHosts") ? "[20.10.10.0..20.10.10.255]" : null; } - }.checkSecurity(LOGGED_NO_ROLE, REMOTE_WITH_DOT); + }.checkSecurity(LOGGED_NO_ROLE, REMOTE_IP); } private static UriInfo uri(final String request) { From b28ec1a13342f738113f26e36ccfe3e6f2d495b8 Mon Sep 17 00:00:00 2001 From: diuis Date: Thu, 27 Feb 2020 15:15:45 +0100 Subject: [PATCH 7/8] add ip range doc and example --- README.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index 50163d2..8f7783d 100644 --- a/README.adoc +++ b/README.adoc @@ -44,7 +44,7 @@ Note that a request without a principal will lead to a HTTP 401 whereas a reques The host validation will use the JAX-RS `UriInfo#getRequestUri`. It relies on the system property `geronimo.metrics.jaxrs.acceptedHosts` and it takes a comma separated list of hosts. At least one must match to let the request pass, if none is set this validation is ignored. -If the host value ends with a dot, the match is a start with match and not an exact match. +If the host value is an IP range (for example [10.10.10.0..10.10.10.255]) the match is true if the client ip is between the two ip address. The `` value is an alias for `127.x.y.z` or `1::x` IP or `localhost`. Configuration example: @@ -52,7 +52,7 @@ Configuration example: [source] ---- -Dgeronimo.metrics.jaxrs.acceptedRoles=ops \ --Dgeronimo.metrics.jaxrs.acceptedHosts=my.remote.host,10.0.0. +-Dgeronimo.metrics.jaxrs.acceptedHosts=my.remote.host,[10.10.10.0..10.10.10.255] ---- IMPORTANT: the default is `geronimo.metrics.jaxrs.acceptedHosts=` but you can disable the endpoints using `geronimo.metrics.jaxrs.activated=false`. From d6b04fe14eb3c6c56ddef03e80bb4dab56993b36 Mon Sep 17 00:00:00 2001 From: diuis Date: Thu, 27 Feb 2020 22:39:00 +0100 Subject: [PATCH 8/8] use the BigInteger to do the between calc with max and min values --- .../metrics/common/jaxrs/SecurityValidator.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java index b9a647a..800edff 100644 --- a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java +++ b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java @@ -16,8 +16,6 @@ */ package org.apache.geronimo.microprofile.metrics.common.jaxrs; -import static java.lang.Math.max; -import static java.lang.Math.min; import static java.util.Collections.singletonList; import static java.util.Optional.ofNullable; import static java.util.function.Function.identity; @@ -61,13 +59,10 @@ public void init() { .filter(values -> values.length == 2) .map(rangeArray -> { try { - long addressMin = new BigInteger(InetAddress.getByName(rangeArray[0]) - .getAddress()).longValue(); - long addressMax = new BigInteger(InetAddress.getByName(rangeArray[1]) - .getAddress()).longValue(); - long addressToValidate = new BigInteger(InetAddress.getByName(ipToValidate) - .getAddress()).longValue(); - return max(addressMin, addressToValidate) == min(addressToValidate, addressMax); + BigInteger addressMin = new BigInteger(InetAddress.getByName(rangeArray[0]).getAddress()); + BigInteger addressMax = new BigInteger(InetAddress.getByName(rangeArray[1]).getAddress()); + BigInteger addressToValidate = new BigInteger(InetAddress.getByName(ipToValidate).getAddress()); + return addressMin.max(addressToValidate).equals(addressMax.min(addressToValidate)); } catch (UnknownHostException e) { return false; }