diff --git a/apps/routerconsole/java/src/net/i2p/router/web/HostCheckHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/HostCheckHandler.java index 0343fe6455..ff862d3bba 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/HostCheckHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/HostCheckHandler.java @@ -11,6 +11,7 @@ import javax.servlet.http.HttpServletResponse; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; import net.i2p.util.Log; +import net.i2p.util.PortMapper; import org.apache.http.conn.util.InetAddressUtils; @@ -29,7 +30,9 @@ import org.eclipse.jetty.server.handler.HandlerWrapper; public class HostCheckHandler extends HandlerWrapper { private final I2PAppContext _context; + private final PortMapper _portMapper; private final Set _listenHosts; + private static final String PROP_REDIRECT = "routerconsole.redirectToHTTPS"; /** * MUST call setListenHosts() afterwards. @@ -37,6 +40,7 @@ public class HostCheckHandler extends HandlerWrapper public HostCheckHandler(I2PAppContext ctx) { super(); _context = ctx; + _portMapper = ctx.portMapper(); _listenHosts = new HashSet(8); } @@ -53,7 +57,9 @@ public class HostCheckHandler extends HandlerWrapper } /** - * Block by Host header, pass everything else to the delegate. + * Block by Host header, + * redirect HTTP to HTTPS, + * pass everything else to the delegate. */ public void handle(String pathInContext, Request baseRequest, @@ -75,6 +81,22 @@ public class HostCheckHandler extends HandlerWrapper return; } + // redirect HTTP to HTTPS if available, AND: + // either 1) PROP_REDIRECT is set to true; + // or 2) PROP_REDIRECT is unset and the Upgrade-Insecure-Requests request header is set + // https://w3c.github.io/webappsec-upgrade-insecure-requests/ + if (!httpRequest.isSecure()) { + int httpsPort = _portMapper.getPort(PortMapper.SVC_HTTPS_CONSOLE); + if (httpsPort > 0 && httpRequest.getLocalPort() != httpsPort) { + String redir = _context.getProperty(PROP_REDIRECT); + if (Boolean.valueOf(redir) || + (redir == null && "1".equals(httpRequest.getHeader("Upgrade-Insecure-Requests")))) { + sendRedirect(httpsPort, httpRequest, httpResponse); + return; + } + } + } + super.handle(pathInContext, baseRequest, httpRequest, httpResponse); } @@ -91,7 +113,11 @@ public class HostCheckHandler extends HandlerWrapper return true; // common cases if (host.equals("127.0.0.1:7657") || - host.equals("localhost:7657")) + host.equals("localhost:7657") || + host.equals("[::1]:7657") || + host.equals("127.0.0.1:7667") || + host.equals("localhost:7667") || + host.equals("[::1]:7667")) return true; // all allowed? if (_listenHosts.isEmpty()) @@ -124,4 +150,32 @@ public class HostCheckHandler extends HandlerWrapper } return host; } + + /** + * Redirect to HTTPS + * + * @since 0.9.34 + */ + private static void sendRedirect(int httpsPort, HttpServletRequest httpRequest, + HttpServletResponse httpResponse) throws IOException { + StringBuilder buf = new StringBuilder(64); + buf.append("https://"); + String name = httpRequest.getServerName(); + boolean ipv6 = name.indexOf(':') >= 0 && !name.startsWith("["); + if (ipv6) + buf.append('['); + buf.append(name); + if (ipv6) + buf.append(']'); + buf.append(':').append(httpsPort) + .append(httpRequest.getRequestURI()); + String q = httpRequest.getQueryString(); + if (q != null) + buf.append('?').append(q); + httpResponse.setHeader("Location", buf.toString()); + // https://w3c.github.io/webappsec-upgrade-insecure-requests/ + httpResponse.setHeader("Vary", "Upgrade-Insecure-Requests"); + httpResponse.setStatus(307); + httpResponse.flushBuffer(); + } } diff --git a/core/java/src/net/i2p/util/PortMapper.java b/core/java/src/net/i2p/util/PortMapper.java index 62790031cd..d9b93b60da 100644 --- a/core/java/src/net/i2p/util/PortMapper.java +++ b/core/java/src/net/i2p/util/PortMapper.java @@ -19,6 +19,7 @@ import net.i2p.I2PAppContext; */ public class PortMapper { private final ConcurrentHashMap _dir; + public static final String PROP_PREFER_HTTPS = "routerconsole.preferHTTPS"; public static final String SVC_CONSOLE = "console"; public static final String SVC_HTTPS_CONSOLE = "https_console"; @@ -177,16 +178,39 @@ public class PortMapper { return rv; } - /* - * @return http URL unless console is https only. Default http://127.0.0.1:7657/ + /** + * If PROP_PREFER_HTTPS is true or unset, + * return https URL unless console is http only. Default https://127.0.0.1:7667/ + * If PROP_PREFER_HTTPS is set to false, + * return http URL unless console is https only. Default http://127.0.0.1:7657/ + * * @since 0.9.33 consolidated from i2ptunnel and desktopgui */ public String getConsoleURL() { + return getConsoleURL(I2PAppContext.getGlobalContext().getBooleanPropertyDefaultTrue(PROP_PREFER_HTTPS)); + } + + /** + * If preferHTTPS is true, + * return https URL unless console is http only. Default https://127.0.0.1:7667/ + * If preferHTTPS is false, + * return http URL unless console is https only. Default http://127.0.0.1:7657/ + * + * @since 0.9.34 + */ + public String getConsoleURL(boolean preferHTTPS) { + return preferHTTPS ? getHTTPSConsoleURL() : getHTTPConsoleURL(); + } + + /** + * @return http URL unless console is https only. Default http://127.0.0.1:7657/ + */ + private String getHTTPConsoleURL() { String unset = "*unset*"; String httpHost = getActualHost(SVC_CONSOLE, unset); String httpsHost = getActualHost(SVC_HTTPS_CONSOLE, unset); int httpPort = getPort(SVC_CONSOLE, 7657); - int httpsPort = getPort(SVC_HTTPS_CONSOLE, -1); + int httpsPort = getPort(SVC_HTTPS_CONSOLE); boolean httpsOnly = httpsPort > 0 && httpHost.equals(unset) && !httpsHost.equals(unset); if (httpsOnly) return "https://" + httpsHost + ':' + httpsPort + '/'; @@ -195,6 +219,24 @@ public class PortMapper { return "http://" + httpHost + ':' + httpPort + '/'; } + /** + * @return https URL unless console is http only. Default https://127.0.0.1:7667/ + * @since 0.9.34 + */ + private String getHTTPSConsoleURL() { + String unset = "*unset*"; + String httpHost = getActualHost(SVC_CONSOLE, unset); + String httpsHost = getActualHost(SVC_HTTPS_CONSOLE, unset); + int httpPort = getPort(SVC_CONSOLE); + int httpsPort = getPort(SVC_HTTPS_CONSOLE, 7667); + boolean httpOnly = httpPort > 0 && httpsHost.equals(unset) && !httpHost.equals(unset); + if (httpOnly) + return "http://" + httpHost + ':' + httpPort + '/'; + if (httpsHost.equals(unset)) + httpsHost = "127.0.0.1"; + return "https://" + httpsHost + ':' + httpsPort + '/'; + } + /** * For debugging only * @since 0.9.20 diff --git a/history.txt b/history.txt index f256f1363c..a1808fcf5b 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,19 @@ +2018-02-20 zzz + * Console: + - Redirect to HTTPS if available (ticket #2160) + - Change all 302s to 303 or 307 + - Change sendError() to setStatus() for 3xx responses + * Crypto: Backdate selfsigned cert to allow for clock skew + * Eepget: Handle 308 + +2018-02-19 zzz + * Console: + - Change trac links (ticket #2014) + - Change selfsigned cert cname to localhost (ticket #2160) + * Crypto: Add IP addresses to selfsigned cert SAN (ticket #2160) + * Streaming: Don't exceed configured tag settings + * Time: More sanity checks on NTP responses + 2018-02-18 zzz * i2ptunnel: Retry accept after router soft restart (ticket #2003) diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index fecba78d69..a6204817e3 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 7; + public final static long BUILD = 8; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/transport/TransportUtil.java b/router/java/src/net/i2p/router/transport/TransportUtil.java index 2f9deeee83..e9864fcd71 100644 --- a/router/java/src/net/i2p/router/transport/TransportUtil.java +++ b/router/java/src/net/i2p/router/transport/TransportUtil.java @@ -239,7 +239,7 @@ public abstract class TransportUtil { port != 6000 && // lockd (!(port >= 6665 && port <= 6669)) && // IRC and alternates port != 6697 && // IRC+TLS - (!(port >= 7650 && port <= 7664)) && // standard I2P range + (!(port >= 7650 && port <= 7668)) && // standard I2P range port != 8998 && // mtn port != 9001 && // Tor port != 9030 && // Tor diff --git a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java index 07f3036012..4783e6661e 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java @@ -117,7 +117,7 @@ class UDPEndpoint implements SocketListener { if (port > 0 && !TransportUtil.isValidPort(port)) { _log.error("Specified UDP port " + port + " is not valid, selecting a new port"); // See isValidPort() for list - _log.error("Invalid ports are: 0-1023, 1900, 2049, 2827, 3659, 4045, 4444, 4445, 6000, 6665-6669, 6697, 7650-7664, 8998, 9001, 9030, 9050, 9100, 9150, 31000, 32000, 65536+"); + _log.error("Invalid ports are: 0-1023, 1900, 2049, 2827, 3659, 4045, 4444, 4445, 6000, 6665-6669, 6697, 7650-7668, 8998, 9001, 9030, 9050, 9100, 9150, 31000, 32000, 65536+"); port = -1; }