Add support for trusted-proxies subnet ranges, handle nested proxies

This commit is contained in:
Mike Primm 2022-02-22 22:11:48 -06:00
parent 037d1803a7
commit a9b4ace851
14 changed files with 132 additions and 15 deletions

View File

@ -5,6 +5,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.dynmap.servlet.ClientUpdateServlet;
import org.dynmap.servlet.SendMessageServlet;
import org.dynmap.utils.IpAddressMatcher;
import org.json.simple.JSONObject;
import static org.dynmap.JSONUtils.*;
@ -65,12 +66,12 @@ public class InternalClientUpdateComponent extends ClientUpdateComponent {
this.core = dcore;
if(trustedproxy != null) {
for(String s : trustedproxy) {
this.proxyaddress.add(s.trim());
this.proxyaddress.add(new IpAddressMatcher(s.trim()));
}
}
else {
this.proxyaddress.add("127.0.0.1");
this.proxyaddress.add("0:0:0:0:0:0:0:1");
this.proxyaddress.add(new IpAddressMatcher("127.0.0.1"));
this.proxyaddress.add(new IpAddressMatcher("0:0:0:0:0:0:0:1"));
}
onMessageReceived.addListener(new Event.Listener<Message> () {
@Override

View File

@ -19,6 +19,7 @@ import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@ -26,6 +27,8 @@ import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
import org.dynmap.utils.IpAddressMatcher;;
@SuppressWarnings("serial")
public class SendMessageServlet extends HttpServlet {
protected static final Logger log = Logger.getLogger("Minecraft");
@ -50,8 +53,16 @@ public class SendMessageServlet extends HttpServlet {
public boolean chat_perms = false;
public int lengthlimit = 256;
public DynmapCore core;
public HashSet<String> proxyaddress = new HashSet<String>();
public ArrayList<IpAddressMatcher> proxyaddress = new ArrayList<IpAddressMatcher>();
private boolean trustedProxy(String ip) {
for (IpAddressMatcher m : proxyaddress) {
if (m.matches(ip)) {
return true;
}
}
return false;
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
byte[] bytes;
@ -92,19 +103,13 @@ public class SendMessageServlet extends HttpServlet {
if ((message.name == null) || message.name.equals("")) {
/* If from trusted proxy, check for client */
String rmtaddr = request.getRemoteAddr();
if (this.proxyaddress.contains(rmtaddr)) {
if (this.trustedProxy(rmtaddr)) { // If remote address is valid trusted proxy
/* If proxied client address, get original IP */
if (request.getHeader("X-Forwarded-For") != null) {
/* If trusted proxies were chained, we get next client address till non-trusted proxy met */
String[] proxyAddrs = request.getHeader("X-Forwarded-For").split(", ");
for(int i = proxyAddrs.length - 1; i >= 0; i--){
if (!this.proxyaddress.contains(proxyAddrs[i])) {
/* use remaining addresses as name (maybe we can use the last or the first non-trusted one?) */
message.name = proxyAddrs[0]; // 0 .. i
for(int j = 1; j <= i; j++) message.name += ", " + proxyAddrs[j];
break;
}
}
// Split list, since addresses after first are proxy chain
String[] proxyAddrs = request.getHeader("X-Forwarded-For").split(",");
// We only want first - any others are proxies that our local proxy was willing to pass to us
message.name = proxyAddrs[0].trim();
} else {
message.name = String.valueOf(o.get("name"));
}

View File

@ -0,0 +1,100 @@
package org.dynmap.utils;
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* Matches a request based on IP Address or subnet mask matching against the remote
* address.
* <p>
* Both IPv6 and IPv4 addresses are supported, but a matcher which is configured with an
* IPv4 address will never match a request which returns an IPv6 address, and vice-versa.
*
* @author Luke Taylor
* @since 3.0.2
*
* Slightly modified by omidzk to have zero dependency to any frameworks other than the JRE.
*/
public final class IpAddressMatcher {
private final int nMaskBits;
private final InetAddress requiredAddress;
/**
* Takes a specific IP address or a range specified using the IP/Netmask (e.g.
* 192.168.1.0/24 or 202.24.0.0/14).
*
* @param ipAddress the address or range of addresses from which the request must
* come.
*/
public IpAddressMatcher(String ipAddress) {
if (ipAddress.indexOf('/') > 0) {
String[] addressAndMask = ipAddress.split("/");
ipAddress = addressAndMask[0];
nMaskBits = Integer.parseInt(addressAndMask[1]);
}
else {
nMaskBits = -1;
}
requiredAddress = parseAddress(ipAddress);
assert (requiredAddress.getAddress().length * 8 >= nMaskBits) :
String.format("IP address %s is too short for bitmask of length %d",
ipAddress, nMaskBits);
}
public boolean matches(String address) {
InetAddress remoteAddress = parseAddress(address);
if (!requiredAddress.getClass().equals(remoteAddress.getClass())) {
return false;
}
if (nMaskBits < 0) {
return remoteAddress.equals(requiredAddress);
}
byte[] remAddr = remoteAddress.getAddress();
byte[] reqAddr = requiredAddress.getAddress();
int nMaskFullBytes = nMaskBits / 8;
byte finalByte = (byte) (0xFF00 >> (nMaskBits & 0x07));
// System.out.println("Mask is " + new sun.misc.HexDumpEncoder().encode(mask));
for (int i = 0; i < nMaskFullBytes; i++) {
if (remAddr[i] != reqAddr[i]) {
return false;
}
}
if (finalByte != 0) {
return (remAddr[nMaskFullBytes] & finalByte) == (reqAddr[nMaskFullBytes] & finalByte);
}
return true;
}
private InetAddress parseAddress(String address) {
try {
return InetAddress.getByName(address);
}
catch (UnknownHostException e) {
throw new IllegalArgumentException("Failed to parse address" + address, e);
}
}
}

View File

@ -391,6 +391,7 @@ grayplayerswhenhidden: true
# X-Custom-Header-Of-Mine: "MyHeaderValue"
# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields
# This now supports both IP address, and subnet ranges (e.g. 192.168.1.0/24 or 202.24.0.0/14 )
trusted-proxies:
- "127.0.0.1"
- "0:0:0:0:0:0:0:1"

View File

@ -391,6 +391,7 @@ grayplayerswhenhidden: true
# X-Custom-Header-Of-Mine: "MyHeaderValue"
# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields
# This now supports both IP address, and subnet ranges (e.g. 192.168.1.0/24 or 202.24.0.0/14 )
trusted-proxies:
- "127.0.0.1"
- "0:0:0:0:0:0:0:1"

View File

@ -399,6 +399,7 @@ grayplayerswhenhidden: true
# X-Custom-Header-Of-Mine: "MyHeaderValue"
# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields
# This now supports both IP address, and subnet ranges (e.g. 192.168.1.0/24 or 202.24.0.0/14 )
trusted-proxies:
- "127.0.0.1"
- "0:0:0:0:0:0:0:1"

View File

@ -399,6 +399,7 @@ grayplayerswhenhidden: true
# X-Custom-Header-Of-Mine: "MyHeaderValue"
# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields
# This now supports both IP address, and subnet ranges (e.g. 192.168.1.0/24 or 202.24.0.0/14 )
trusted-proxies:
- "127.0.0.1"
- "0:0:0:0:0:0:0:1"

View File

@ -397,6 +397,7 @@ grayplayerswhenhidden: true
# X-Custom-Header-Of-Mine: "MyHeaderValue"
# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields
# This now supports both IP address, and subnet ranges (e.g. 192.168.1.0/24 or 202.24.0.0/14 )
trusted-proxies:
- "127.0.0.1"
- "0:0:0:0:0:0:0:1"

View File

@ -399,6 +399,7 @@ grayplayerswhenhidden: true
# X-Custom-Header-Of-Mine: "MyHeaderValue"
# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields
# This now supports both IP address, and subnet ranges (e.g. 192.168.1.0/24 or 202.24.0.0/14 )
trusted-proxies:
- "127.0.0.1"
- "0:0:0:0:0:0:0:1"

View File

@ -399,6 +399,7 @@ grayplayerswhenhidden: true
# X-Custom-Header-Of-Mine: "MyHeaderValue"
# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields
# This now supports both IP address, and subnet ranges (e.g. 192.168.1.0/24 or 202.24.0.0/14 )
trusted-proxies:
- "127.0.0.1"
- "0:0:0:0:0:0:0:1"

View File

@ -399,6 +399,7 @@ grayplayerswhenhidden: true
# X-Custom-Header-Of-Mine: "MyHeaderValue"
# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields
# This now supports both IP address, and subnet ranges (e.g. 192.168.1.0/24 or 202.24.0.0/14 )
trusted-proxies:
- "127.0.0.1"
- "0:0:0:0:0:0:0:1"

View File

@ -399,6 +399,7 @@ grayplayerswhenhidden: true
# X-Custom-Header-Of-Mine: "MyHeaderValue"
# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields
# This now supports both IP address, and subnet ranges (e.g. 192.168.1.0/24 or 202.24.0.0/14 )
trusted-proxies:
- "127.0.0.1"
- "0:0:0:0:0:0:0:1"

View File

@ -399,6 +399,7 @@ grayplayerswhenhidden: true
# X-Custom-Header-Of-Mine: "MyHeaderValue"
# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields
# This now supports both IP address, and subnet ranges (e.g. 192.168.1.0/24 or 202.24.0.0/14 )
trusted-proxies:
- "127.0.0.1"
- "0:0:0:0:0:0:0:1"

View File

@ -411,6 +411,7 @@ player-sort-permission-nodes:
# X-Custom-Header-Of-Mine: "MyHeaderValue"
# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields
# This now supports both IP address, and subnet ranges (e.g. 192.168.1.0/24 or 202.24.0.0/14 )
trusted-proxies:
- "127.0.0.1"
- "0:0:0:0:0:0:0:1"