From a34e44ad5a2e088f8483b0281eebd30f4ed63030 Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Thu, 10 Apr 2014 11:09:57 -0400 Subject: [PATCH] Merge pull request #167 from Hybris95/master --- src/main/java/com/gitblit/GitBlitServer.java | 215 ++++++++++++++++++++++++++++++++++++----------------- 1 files changed, 144 insertions(+), 71 deletions(-) diff --git a/src/main/java/com/gitblit/GitBlitServer.java b/src/main/java/com/gitblit/GitBlitServer.java index 79d3d6b..64d3cad 100644 --- a/src/main/java/com/gitblit/GitBlitServer.java +++ b/src/main/java/com/gitblit/GitBlitServer.java @@ -20,6 +20,7 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetAddress; @@ -33,9 +34,13 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Properties; import java.util.Scanner; +import org.apache.log4j.PropertyConfigurator; import org.eclipse.jetty.ajp.Ajp13SocketConnector; +import org.eclipse.jetty.security.ConstraintMapping; +import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.bio.SocketConnector; @@ -44,20 +49,21 @@ import org.eclipse.jetty.server.ssl.SslConnector; import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; import org.eclipse.jetty.server.ssl.SslSocketConnector; +import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FileUtils; +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.Option; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.Parameters; import com.gitblit.authority.GitblitAuthority; import com.gitblit.authority.NewCertificateConfig; +import com.gitblit.servlet.GitblitContext; import com.gitblit.utils.StringUtils; import com.gitblit.utils.TimeUtils; import com.gitblit.utils.X509Utils; @@ -75,9 +81,9 @@ * simplify command line parameter processing. This class also automatically * generates a self-signed certificate for localhost, if the keystore does not * already exist. - * + * * @author James Moger - * + * */ public class GitBlitServer { @@ -85,17 +91,17 @@ public static void main(String... args) { GitBlitServer server = new GitBlitServer(); - + // filter out the baseFolder parameter List<String> filtered = new ArrayList<String>(); String folder = "data"; - for (int i = 0; i< args.length; i++) { + for (int i = 0; i < args.length; i++) { String arg = args[i]; if (arg.equals("--baseFolder")) { if (i + 1 == args.length) { System.out.println("Invalid --baseFolder parameter!"); System.exit(-1); - } else if (args[i + 1] != ".") { + } else if (!".".equals(args[i + 1])) { folder = args[i + 1]; } i = i + 1; @@ -103,17 +109,17 @@ filtered.add(arg); } } - + Params.baseFolder = folder; Params params = new Params(); - JCommander jc = new JCommander(params); + CmdLineParser parser = new CmdLineParser(params); try { - jc.parse(filtered.toArray(new String[filtered.size()])); + parser.parseArgument(filtered); if (params.help) { - server.usage(jc, null); + server.usage(parser, null); } - } catch (ParameterException t) { - server.usage(jc, t); + } catch (CmdLineException t) { + server.usage(parser, t); } if (params.stop) { @@ -125,11 +131,11 @@ /** * Display the command line usage of Gitblit GO. - * - * @param jc + * + * @param parser * @param t */ - protected final void usage(JCommander jc, ParameterException t) { + protected final void usage(CmdLineParser parser, CmdLineException t) { System.out.println(Constants.BORDER); System.out.println(Constants.getGitBlitVersion()); System.out.println(Constants.BORDER); @@ -138,8 +144,8 @@ System.out.println(t.getMessage()); System.out.println(); } - if (jc != null) { - jc.usage(); + if (parser != null) { + parser.printUsage(System.out); System.out .println("\nExample:\n java -server -Xmx1024M -jar gitblit.jar --repositoriesFolder c:\\git --httpPort 80 --httpsPort 443"); } @@ -172,9 +178,37 @@ FileSettings settings = params.FILESETTINGS; if (!StringUtils.isEmpty(params.settingsfile)) { if (new File(params.settingsfile).exists()) { - settings = new FileSettings(params.settingsfile); + settings = new FileSettings(params.settingsfile); } } + + if (params.dailyLogFile) { + // Configure log4j for daily log file generation + InputStream is = null; + try { + is = getClass().getResourceAsStream("/log4j.properties"); + Properties loggingProperties = new Properties(); + loggingProperties.load(is); + + loggingProperties.put("log4j.appender.R.File", new File(baseFolder, "logs/gitblit.log").getAbsolutePath()); + loggingProperties.put("log4j.rootCategory", "INFO, R"); + + if (settings.getBoolean(Keys.web.debugMode, false)) { + loggingProperties.put("log4j.logger.com.gitblit", "DEBUG"); + } + + PropertyConfigurator.configure(loggingProperties); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + logger = LoggerFactory.getLogger(GitBlitServer.class); logger.info(Constants.BORDER); logger.info(" _____ _ _ _ _ _ _"); @@ -198,12 +232,12 @@ String osname = System.getProperty("os.name"); String osversion = System.getProperty("os.version"); logger.info("Running on " + osname + " (" + osversion + ")"); - + List<Connector> connectors = new ArrayList<Connector>(); // conditionally configure the http connector if (params.port > 0) { - Connector httpConnector = createConnector(params.useNIO, params.port); + Connector httpConnector = createConnector(params.useNIO, params.port, settings.getInteger(Keys.server.threadPoolSize, 50)); String bindInterface = settings.getString(Keys.server.httpBindInterface, null); if (!StringUtils.isEmpty(bindInterface)) { logger.warn(MessageFormat.format("Binding connector on port {0,number,0} to {1}", @@ -212,6 +246,14 @@ } if (params.port < 1024 && !isWindows()) { logger.warn("Gitblit needs to run with ROOT permissions for ports < 1024!"); + } + if (params.port > 0 && params.securePort > 0 && settings.getBoolean(Keys.server.redirectToHttpsPort, true)) { + // redirect HTTP requests to HTTPS + if (httpConnector instanceof SelectChannelConnector) { + ((SelectChannelConnector) httpConnector).setConfidentialPort(params.securePort); + } else { + ((SocketConnector) httpConnector).setConfidentialPort(params.securePort); + } } connectors.add(httpConnector); } @@ -236,7 +278,7 @@ NewCertificateConfig certificateConfig = NewCertificateConfig.KEY.parse(config); certificateConfig.update(metadata); } - + metadata.notAfter = new Date(System.currentTimeMillis() + 10*TimeUtils.ONEYEAR); X509Utils.prepareX509Infrastructure(metadata, baseFolder, new X509Log() { @Override @@ -260,9 +302,9 @@ } }); - if (serverKeyStore.exists()) { + if (serverKeyStore.exists()) { Connector secureConnector = createSSLConnector(params.alias, serverKeyStore, serverTrustStore, params.storePassword, - caRevocationList, params.useNIO, params.securePort, params.requireClientCertificates); + caRevocationList, params.useNIO, params.securePort, settings.getInteger(Keys.server.threadPoolSize, 50), params.requireClientCertificates); String bindInterface = settings.getString(Keys.server.httpsBindInterface, null); if (!StringUtils.isEmpty(bindInterface)) { logger.warn(MessageFormat.format( @@ -297,7 +339,7 @@ // tempDir is where the embedded Gitblit web application is expanded and // where Jetty creates any necessary temporary files - File tempDir = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, baseFolder, params.temp); + File tempDir = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, baseFolder, params.temp); if (tempDir.exists()) { try { FileUtils.delete(tempDir, FileUtils.RECURSIVE | FileUtils.RETRY); @@ -329,7 +371,7 @@ HashSessionManager sessionManager = new HashSessionManager(); sessionManager.setHttpOnly(true); // Use secure cookies if only serving https - sessionManager.setSecureCookies(params.port <= 0 && params.securePort > 0); + sessionManager.setSecureRequestOnly(params.port <= 0 && params.securePort > 0); rootContext.getSessionHandler().setSessionManager(sessionManager); // Ensure there is a defined User Service @@ -343,10 +385,10 @@ settings.overrideSetting(Keys.realm.userService, params.userService); settings.overrideSetting(Keys.git.repositoriesFolder, params.repositoriesFolder); settings.overrideSetting(Keys.git.daemonPort, params.gitPort); - + // Start up an in-memory LDAP server, if configured try { - if (StringUtils.isEmpty(params.ldapLdifFile) == false) { + if (!StringUtils.isEmpty(params.ldapLdifFile)) { File ldifFile = new File(params.ldapLdifFile); if (ldifFile != null && ldifFile.exists()) { URI ldapUrl = new URI(settings.getRequiredString(Keys.realm.ldap.server)); @@ -354,21 +396,21 @@ String rootDN = firstLine.substring(4); String bindUserName = settings.getString(Keys.realm.ldap.username, ""); String bindPassword = settings.getString(Keys.realm.ldap.password, ""); - + // Get the port int port = ldapUrl.getPort(); if (port == -1) port = 389; - + InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(rootDN); config.addAdditionalBindCredentials(bindUserName, bindPassword); config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("default", port)); config.setSchema(null); - + InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config); ds.importFromLDIF(true, new LDIFReader(ldifFile)); ds.startListening(); - + logger.info("LDAP Server started at ldap://localhost:" + port); } } @@ -380,9 +422,26 @@ // Set the server's contexts server.setHandler(rootContext); - // Setup the GitBlit context - GitBlit gitblit = getGitBlitInstance(); - gitblit.configureContext(settings, baseFolder, true); + // redirect HTTP requests to HTTPS + if (params.port > 0 && params.securePort > 0 && settings.getBoolean(Keys.server.redirectToHttpsPort, true)) { + logger.info(String.format("Configuring automatic http(%1$s) -> https(%2$s) redirects", params.port, params.securePort)); + // Create the internal mechanisms to handle secure connections and redirects + Constraint constraint = new Constraint(); + constraint.setDataConstraint(Constraint.DC_CONFIDENTIAL); + + ConstraintMapping cm = new ConstraintMapping(); + cm.setConstraint(constraint); + cm.setPathSpec("/*"); + + ConstraintSecurityHandler sh = new ConstraintSecurityHandler(); + sh.setConstraintMappings(new ConstraintMapping[] { cm }); + + // Configure this context to use the Security Handler defined before + rootContext.setHandler(sh); + } + + // Setup the Gitblit context + GitblitContext gitblit = newGitblit(settings, baseFolder); rootContext.addEventListener(gitblit); try { @@ -400,29 +459,35 @@ System.exit(100); } } - - protected GitBlit getGitBlitInstance() { - return GitBlit.self(); + + protected GitblitContext newGitblit(IStoredSettings settings, File baseFolder) { + return new GitblitContext(settings, baseFolder); } /** * Creates an http connector. - * + * * @param useNIO * @param port + * @param threadPoolSize * @return an http connector */ - private Connector createConnector(boolean useNIO, int port) { + private Connector createConnector(boolean useNIO, int port, int threadPoolSize) { Connector connector; if (useNIO) { logger.info("Setting up NIO SelectChannelConnector on port " + port); SelectChannelConnector nioconn = new SelectChannelConnector(); nioconn.setSoLingerTime(-1); - nioconn.setThreadPool(new QueuedThreadPool(20)); + if (threadPoolSize > 0) { + nioconn.setThreadPool(new QueuedThreadPool(threadPoolSize)); + } connector = nioconn; } else { logger.info("Setting up SocketConnector on port " + port); SocketConnector sockconn = new SocketConnector(); + if (threadPoolSize > 0) { + sockconn.setThreadPool(new QueuedThreadPool(threadPoolSize)); + } connector = sockconn; } @@ -433,10 +498,10 @@ /** * Creates an https connector. - * + * * SSL renegotiation will be enabled if the JVM is 1.6.0_22 or later. * oracle.com/technetwork/java/javase/documentation/tlsreadme2-176330.html - * + * * @param certAlias * @param keyStore * @param clientTrustStore @@ -444,11 +509,12 @@ * @param caRevocationList * @param useNIO * @param port + * @param threadPoolSize * @param requireClientCertificates * @return an https connector */ private Connector createSSLConnector(String certAlias, File keyStore, File clientTrustStore, - String storePassword, File caRevocationList, boolean useNIO, int port, + String storePassword, File caRevocationList, boolean useNIO, int port, int threadPoolSize, boolean requireClientCertificates) { GitblitSslContextFactory factory = new GitblitSslContextFactory(certAlias, keyStore, clientTrustStore, storePassword, caRevocationList); @@ -462,11 +528,16 @@ } else { factory.setWantClientAuth(true); } - ssl.setThreadPool(new QueuedThreadPool(20)); + if (threadPoolSize > 0) { + ssl.setThreadPool(new QueuedThreadPool(threadPoolSize)); + } connector = ssl; } else { logger.info("Setting up NIO SslSocketConnector on port " + port); SslSocketConnector ssl = new SslSocketConnector(factory); + if (threadPoolSize > 0) { + ssl.setThreadPool(new QueuedThreadPool(threadPoolSize)); + } connector = ssl; } connector.setPort(port); @@ -474,10 +545,10 @@ return connector; } - + /** * Creates an ajp connector. - * + * * @param port * @return an ajp connector */ @@ -493,7 +564,7 @@ /** * Tests to see if the operating system is Windows. - * + * * @return true if this is a windows machine */ private boolean isWindows() { @@ -504,9 +575,9 @@ * The ShutdownMonitorThread opens a socket on a specified port and waits * for an incoming connection. When that connection is accepted a shutdown * message is issued to the running Jetty server. - * + * * @author James Moger - * + * */ private static class ShutdownMonitorThread extends Thread { @@ -552,9 +623,8 @@ } /** - * JCommander Parameters class for GitBlitServer. + * Parameters class for GitBlitServer. */ - @Parameters(separators = " ") public static class Params { public static String baseFolder; @@ -564,66 +634,69 @@ /* * Server parameters */ - @Parameter(names = { "-h", "--help" }, description = "Show this help") + @Option(name = "--help", aliases = { "-h"}, usage = "Show this help") public Boolean help = false; - @Parameter(names = { "--stop" }, description = "Stop Server") + @Option(name = "--stop", usage = "Stop Server") public Boolean stop = false; - @Parameter(names = { "--tempFolder" }, description = "Folder for server to extract built-in webapp") + @Option(name = "--tempFolder", usage = "Folder for server to extract built-in webapp", metaVar="PATH") public String temp = FILESETTINGS.getString(Keys.server.tempFolder, "temp"); + + @Option(name = "--dailyLogFile", usage = "Log to a rolling daily log file INSTEAD of stdout.") + public Boolean dailyLogFile = false; /* * GIT Servlet Parameters */ - @Parameter(names = { "--repositoriesFolder" }, description = "Git Repositories Folder") + @Option(name = "--repositoriesFolder", usage = "Git Repositories Folder", metaVar="PATH") public String repositoriesFolder = FILESETTINGS.getString(Keys.git.repositoriesFolder, "git"); /* * Authentication Parameters */ - @Parameter(names = { "--userService" }, description = "Authentication and Authorization Service (filename or fully qualified classname)") + @Option(name = "--userService", usage = "Authentication and Authorization Service (filename or fully qualified classname)") public String userService = FILESETTINGS.getString(Keys.realm.userService, "users.conf"); /* * JETTY Parameters */ - @Parameter(names = { "--useNio" }, description = "Use NIO Connector else use Socket Connector.") + @Option(name = "--useNio", usage = "Use NIO Connector else use Socket Connector.") public Boolean useNIO = FILESETTINGS.getBoolean(Keys.server.useNio, true); - @Parameter(names = "--httpPort", description = "HTTP port for to serve. (port <= 0 will disable this connector)") + @Option(name = "--httpPort", usage = "HTTP port for to serve. (port <= 0 will disable this connector)", metaVar="PORT") public Integer port = FILESETTINGS.getInteger(Keys.server.httpPort, 0); - @Parameter(names = "--httpsPort", description = "HTTPS port to serve. (port <= 0 will disable this connector)") + @Option(name = "--httpsPort", usage = "HTTPS port to serve. (port <= 0 will disable this connector)", metaVar="PORT") public Integer securePort = FILESETTINGS.getInteger(Keys.server.httpsPort, 8443); - @Parameter(names = "--ajpPort", description = "AJP port to serve. (port <= 0 will disable this connector)") + @Option(name = "--ajpPort", usage = "AJP port to serve. (port <= 0 will disable this connector)", metaVar="PORT") public Integer ajpPort = FILESETTINGS.getInteger(Keys.server.ajpPort, 0); - @Parameter(names = "--gitPort", description = "Git Daemon port to serve. (port <= 0 will disable this connector)") + @Option(name = "--gitPort", usage = "Git Daemon port to serve. (port <= 0 will disable this connector)", metaVar="PORT") public Integer gitPort = FILESETTINGS.getInteger(Keys.git.daemonPort, 9418); - @Parameter(names = "--alias", description = "Alias of SSL certificate in keystore for serving https.") + @Option(name = "--alias", usage = "Alias of SSL certificate in keystore for serving https.", metaVar="ALIAS") public String alias = FILESETTINGS.getString(Keys.server.certificateAlias, ""); - @Parameter(names = "--storePassword", description = "Password for SSL (https) keystore.") + @Option(name = "--storePassword", usage = "Password for SSL (https) keystore.", metaVar="PASSWORD") public String storePassword = FILESETTINGS.getString(Keys.server.storePassword, ""); - @Parameter(names = "--shutdownPort", description = "Port for Shutdown Monitor to listen on. (port <= 0 will disable this monitor)") + @Option(name = "--shutdownPort", usage = "Port for Shutdown Monitor to listen on. (port <= 0 will disable this monitor)", metaVar="PORT") public Integer shutdownPort = FILESETTINGS.getInteger(Keys.server.shutdownPort, 8081); - @Parameter(names = "--requireClientCertificates", description = "Require client X509 certificates for https connections.") + @Option(name = "--requireClientCertificates", usage = "Require client X509 certificates for https connections.") public Boolean requireClientCertificates = FILESETTINGS.getBoolean(Keys.server.requireClientCertificates, false); /* * Setting overrides */ - @Parameter(names = { "--settings" }, description = "Path to alternative settings") + @Option(name = "--settings", usage = "Path to alternative settings", metaVar="FILE") public String settingsfile; - - @Parameter(names = { "--ldapLdifFile" }, description = "Path to LDIF file. This will cause an in-memory LDAP server to be started according to gitblit settings") + + @Option(name = "--ldapLdifFile", usage = "Path to LDIF file. This will cause an in-memory LDAP server to be started according to gitblit settings", metaVar="FILE") public String ldapLdifFile; } -- Gitblit v1.9.1