mirror of
				https://github.com/gitbucket/gitbucket.git
				synced 2025-10-31 10:36:05 +01:00 
			
		
		
		
	Add HTTPS support for JettyLauncher (#2896)
This commit is contained in:
		| @@ -1,30 +1,66 @@ | ||||
| import org.eclipse.jetty.server.ConnectionFactory; | ||||
| import org.eclipse.jetty.server.Connector; | ||||
| import org.eclipse.jetty.http.HttpVersion; | ||||
| import org.eclipse.jetty.server.Handler; | ||||
| import org.eclipse.jetty.server.HttpConfiguration; | ||||
| import org.eclipse.jetty.server.HttpConnectionFactory; | ||||
| import org.eclipse.jetty.server.SecureRequestCustomizer; | ||||
| import org.eclipse.jetty.server.Server; | ||||
| import org.eclipse.jetty.server.ServerConnector; | ||||
| import org.eclipse.jetty.server.SslConnectionFactory; | ||||
| import org.eclipse.jetty.server.handler.HandlerList; | ||||
| import org.eclipse.jetty.server.handler.SecuredRedirectHandler; | ||||
| import org.eclipse.jetty.server.handler.StatisticsHandler; | ||||
| import org.eclipse.jetty.server.session.DefaultSessionCache; | ||||
| import org.eclipse.jetty.server.session.FileSessionDataStore; | ||||
| import org.eclipse.jetty.server.session.SessionCache; | ||||
| import org.eclipse.jetty.server.session.SessionHandler; | ||||
| import org.eclipse.jetty.util.ssl.SslContextFactory; | ||||
| import org.eclipse.jetty.webapp.WebAppContext; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.net.InetAddress; | ||||
| import java.net.URL; | ||||
| import java.net.InetSocketAddress; | ||||
| import java.security.ProtectionDomain; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
| import java.util.function.Function; | ||||
| import java.util.stream.Stream; | ||||
|  | ||||
| import static java.util.function.Function.identity; | ||||
| import static java.util.stream.Collectors.toSet; | ||||
|  | ||||
| public class JettyLauncher { | ||||
|  | ||||
|     private interface Defaults { | ||||
|  | ||||
|         String CONNECTORS = "http"; | ||||
|         String HOST = "0.0.0.0"; | ||||
|  | ||||
|         int HTTP_PORT = 8080; | ||||
|         int HTTPS_PORT = 8443; | ||||
|  | ||||
|         boolean REDIRECT_HTTPS = false; | ||||
|     } | ||||
|  | ||||
|     private interface Connectors { | ||||
|  | ||||
|         String HTTP = "http"; | ||||
|         String HTTPS = "https"; | ||||
|     } | ||||
|  | ||||
|     public static void main(String[] args) throws Exception { | ||||
|         System.setProperty("java.awt.headless", "true"); | ||||
|  | ||||
|         String connectors = getEnvironmentVariable("gitbucket.connectors"); | ||||
|         String host = getEnvironmentVariable("gitbucket.host"); | ||||
|         String port = getEnvironmentVariable("gitbucket.port"); | ||||
|         InetSocketAddress address; | ||||
|         String securePort = getEnvironmentVariable("gitbucket.securePort"); | ||||
|         String keyStorePath = getEnvironmentVariable("gitbucket.keyStorePath"); | ||||
|         String keyStorePassword = getEnvironmentVariable("gitbucket.keyStorePassword"); | ||||
|         String keyManagerPassword = getEnvironmentVariable("gitbucket.keyManagerPassword"); | ||||
|         String redirectHttps = getEnvironmentVariable("gitbucket.redirectHttps"); | ||||
|         String contextPath = getEnvironmentVariable("gitbucket.prefix"); | ||||
|         String tmpDirPath = getEnvironmentVariable("gitbucket.tempDir"); | ||||
|         boolean forceHttps = false; | ||||
|         boolean saveSessions = false; | ||||
|  | ||||
|         for(String arg: args) { | ||||
| @@ -32,15 +68,33 @@ public class JettyLauncher { | ||||
|                 saveSessions = true; | ||||
|             } | ||||
|             if(arg.startsWith("--") && arg.contains("=")) { | ||||
|                 String[] dim = arg.split("="); | ||||
|                 if(dim.length >= 2) { | ||||
|                 String[] dim = arg.split("=", 2); | ||||
|                 if(dim.length == 2) { | ||||
|                     switch (dim[0]) { | ||||
|                         case "--connectors": | ||||
|                             connectors = dim[1]; | ||||
|                             break; | ||||
|                         case "--host": | ||||
|                             host = dim[1]; | ||||
|                             break; | ||||
|                         case "--port": | ||||
|                             port = dim[1]; | ||||
|                             break; | ||||
|                         case "--secure_port": | ||||
|                             securePort = dim[1]; | ||||
|                             break; | ||||
|                         case "--key_store_path": | ||||
|                             keyStorePath = dim[1]; | ||||
|                             break; | ||||
|                         case "--key_store_password": | ||||
|                             keyStorePassword = dim[1]; | ||||
|                             break; | ||||
|                         case "--key_manager_password": | ||||
|                             keyManagerPassword = dim[1]; | ||||
|                             break; | ||||
|                         case "--redirect_https": | ||||
|                             redirectHttps = dim[1]; | ||||
|                             break; | ||||
|                         case "--prefix": | ||||
|                             contextPath = dim[1]; | ||||
|                             break; | ||||
| @@ -62,38 +116,69 @@ public class JettyLauncher { | ||||
|             contextPath = "/" + contextPath; | ||||
|         } | ||||
|  | ||||
|         if(host != null) { | ||||
|             address = new InetSocketAddress(host, getPort(port)); | ||||
|         } else { | ||||
|             address = new InetSocketAddress(getPort(port)); | ||||
|         final String hostName = InetAddress.getByName(fallback(host, Defaults.HOST)).getHostName(); | ||||
|  | ||||
|         final Server server = new Server(); | ||||
|  | ||||
|         final Set<String> connectorsSet = Stream.of(fallback(connectors, Defaults.CONNECTORS) | ||||
|             .toLowerCase().split(",")).map(String::trim).collect(toSet()); | ||||
|  | ||||
|         final List<ServerConnector> connectorInstances = new ArrayList<>(); | ||||
|  | ||||
|         final HttpConfiguration httpConfig = new HttpConfiguration(); | ||||
|         httpConfig.setSendServerVersion(false); | ||||
|         if (connectorsSet.contains(Connectors.HTTPS)) { | ||||
|             httpConfig.setSecurePort(fallback(securePort, Defaults.HTTPS_PORT, Integer::parseInt)); | ||||
|         } | ||||
|  | ||||
|         Server server = new Server(address); | ||||
|         if (connectorsSet.contains(Connectors.HTTP)) { | ||||
|             final ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConfig)); | ||||
|             connector.setHost(hostName); | ||||
|             connector.setPort(fallback(port, Defaults.HTTP_PORT, Integer::parseInt)); | ||||
|  | ||||
| //        SelectChannelConnector connector = new SelectChannelConnector(); | ||||
| //        if(host != null) { | ||||
| //            connector.setHost(host); | ||||
| //        } | ||||
| //        connector.setMaxIdleTime(1000 * 60 * 60); | ||||
| //        connector.setSoLingerTime(-1); | ||||
| //        connector.setPort(port); | ||||
| //        server.addConnector(connector); | ||||
|  | ||||
|         // Disabling Server header | ||||
|         for (Connector connector : server.getConnectors()) { | ||||
|             for (ConnectionFactory factory : connector.getConnectionFactories()) { | ||||
|                 if (factory instanceof HttpConnectionFactory) { | ||||
|                     ((HttpConnectionFactory) factory).getHttpConfiguration().setSendServerVersion(false); | ||||
|                 } | ||||
|             } | ||||
|             connectorInstances.add(connector); | ||||
|         } | ||||
|  | ||||
|         if (connectorsSet.contains(Connectors.HTTPS)) { | ||||
|             final SslContextFactory sslContextFactory = new SslContextFactory.Server(); | ||||
|  | ||||
|             sslContextFactory.setKeyStorePath(requireNonNull(keyStorePath, | ||||
|                 "You must specify a path to an SSL keystore via the --key_store_path command line argument" + | ||||
|                     " or GITBUCKET_KEYSTOREPATH environment variable.")); | ||||
|  | ||||
|             sslContextFactory.setKeyStorePassword(requireNonNull(keyStorePassword, | ||||
|                 "You must specify a an SSL keystore password via the --key_store_password argument" + | ||||
|                     " or GITBUCKET_KEYSTOREPASSWORD environment variable.")); | ||||
|  | ||||
|             sslContextFactory.setKeyManagerPassword(requireNonNull(keyManagerPassword, | ||||
|                 "You must specify a key manager password via the --key_manager_password' argument" + | ||||
|                     " or GITBUCKET_KEYMANAGERPASSWORD environment variable.")); | ||||
|  | ||||
|             final HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); | ||||
|             httpsConfig.addCustomizer(new SecureRequestCustomizer()); | ||||
|  | ||||
|             final ServerConnector connector = new ServerConnector(server, | ||||
|                 new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), | ||||
|                 new HttpConnectionFactory(httpsConfig)); | ||||
|  | ||||
|             connector.setHost(hostName); | ||||
|             connector.setPort(fallback(securePort, Defaults.HTTPS_PORT, Integer::parseInt)); | ||||
|  | ||||
|             connectorInstances.add(connector); | ||||
|         } | ||||
|  | ||||
|         require(!connectorInstances.isEmpty(), | ||||
|             "No server connectors could be configured, please check your --connectors command line argument" + | ||||
|                 " or GITBUCKET_CONNECTORS environment variable."); | ||||
|  | ||||
|         server.setConnectors(connectorInstances.toArray(new ServerConnector[0])); | ||||
|  | ||||
|         WebAppContext context = new WebAppContext(); | ||||
|  | ||||
|         if(saveSessions) { | ||||
|             File sessDir = new File(getGitBucketHome(), "sessions"); | ||||
|             if(!sessDir.exists()){ | ||||
|                 sessDir.mkdirs(); | ||||
|                 mkdir(sessDir); | ||||
|             } | ||||
|             SessionHandler sessions = context.getSessionHandler(); | ||||
|             SessionCache cache = new DefaultSessionCache(sessions); | ||||
| @@ -107,7 +192,7 @@ public class JettyLauncher { | ||||
|         if(tmpDirPath == null || tmpDirPath.equals("")){ | ||||
|             tmpDir = new File(getGitBucketHome(), "tmp"); | ||||
|             if(!tmpDir.exists()){ | ||||
|                 tmpDir.mkdirs(); | ||||
|                 mkdir(tmpDir); | ||||
|             } | ||||
|         } else { | ||||
|             tmpDir = new File(tmpDirPath); | ||||
| @@ -131,13 +216,16 @@ public class JettyLauncher { | ||||
|         context.setDescriptor(location.toExternalForm() + "/WEB-INF/web.xml"); | ||||
|         context.setServer(server); | ||||
|         context.setWar(location.toExternalForm()); | ||||
|         if (forceHttps) { | ||||
|             context.setInitParameter("org.scalatra.ForceHttps", "true"); | ||||
|  | ||||
|         final HandlerList handlers = new HandlerList(); | ||||
|  | ||||
|         if (fallback(redirectHttps, Defaults.REDIRECT_HTTPS, Boolean::parseBoolean)) { | ||||
|             handlers.addHandler(new SecuredRedirectHandler()); | ||||
|         } | ||||
|  | ||||
|         Handler handler = addStatisticsHandler(context); | ||||
|         handlers.addHandler(addStatisticsHandler(context)); | ||||
|  | ||||
|         server.setHandler(handler); | ||||
|         server.setHandler(handlers); | ||||
|         server.setStopAtShutdown(true); | ||||
|         server.setStopTimeout(7_000); | ||||
|         server.start(); | ||||
| @@ -165,11 +253,28 @@ public class JettyLauncher { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static int getPort(String port){ | ||||
|         if(port == null) { | ||||
|             return 8080; | ||||
|         } else { | ||||
|             return Integer.parseInt(port); | ||||
|     private static <T, R> T fallback(R value, T defaultValue, Function<R, T> converter) { | ||||
|         return value == null ? defaultValue : converter.apply(value); | ||||
|     } | ||||
|  | ||||
|     private static <T> T fallback(T value, T defaultValue) { | ||||
|         return fallback(value, defaultValue, identity()); | ||||
|     } | ||||
|  | ||||
|     private static void require(boolean condition, String message) { | ||||
|         if (!condition) { | ||||
|             throw new IllegalArgumentException(message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static <T> T requireNonNull(T value, String message) { | ||||
|         require(value != null, message); | ||||
|         return value; | ||||
|     } | ||||
|  | ||||
|     private static void mkdir(File dir) { | ||||
|         if (!dir.mkdirs()) { | ||||
|             throw new RuntimeException("Unable to create directory: " + dir); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user