/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.web; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.SecureRandom; import java.security.Security; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Date; import java.util.Enumeration; import java.util.logging.Level; import java.util.logging.Logger; import javax.security.auth.x500.X500Principal; import org.bouncycastle.jce.X509Principal; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.x509.X509V3CertificateGenerator; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.Connector; 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.util.ssl.SslContextFactory; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.xml.XmlConfiguration; import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor; /** * Jetty starter, will run geoserver inside the Jetty web container.<br> * Useful for debugging, especially in IDE were you have direct dependencies * between the sources of the various modules (such as Eclipse). * * @author wolf * */ public class Start { private static final Logger log = org.geotools.util.logging.Logging.getLogger(Start.class.getName()); public static void main(String[] args) { final Server jettyServer = new Server(); try { HttpConfiguration httpConfig = new HttpConfiguration(); ServerConnector http = new ServerConnector(jettyServer, new HttpConnectionFactory(httpConfig)); http.setPort(Integer.getInteger("jetty.port", 8080)); http.setAcceptQueueSize(100); http.setIdleTimeout(1000 * 60 * 60); http.setSoLingerTime(-1); // Use this to set a limit on the number of threads used to respond requests // BoundedThreadPool tp = new BoundedThreadPool(); // tp.setMinThreads(8); // tp.setMaxThreads(8); // conn.setThreadPool(tp); // SSL host name given ? String sslHost = System.getProperty("ssl.hostname"); ServerConnector https = null; if (sslHost!=null && sslHost.length()>0) { Security.addProvider(new BouncyCastleProvider()); SslContextFactory ssl = createSSLContextFactory(sslHost); HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); httpsConfig.addCustomizer(new SecureRequestCustomizer()); https = new ServerConnector(jettyServer, new SslConnectionFactory(ssl, HttpVersion.HTTP_1_1.asString()), new HttpConnectionFactory(httpsConfig)); https.setPort(8443); } jettyServer.setConnectors(https != null ? new Connector[]{http, https} : new Connector[]{http}); /*Constraint constraint = new Constraint(); constraint.setName(Constraint.__BASIC_AUTH);; constraint.setRoles(new String[]{"user","admin","moderator"}); constraint.setAuthenticate(true); ConstraintMapping cm = new ConstraintMapping(); cm.setConstraint(constraint); cm.setPathSpec("/*"); SecurityHandler sh = new SecurityHandler(); sh.setUserRealm(new HashUserRealm("MyRealm","/Users/jdeolive/realm.properties")); sh.setConstraintMappings(new ConstraintMapping[]{cm}); WebAppContext wah = new WebAppContext(sh, null, null, null);*/ WebAppContext wah = new WebAppContext(); wah.setContextPath("/geoserver"); wah.setWar("src/main/webapp"); jettyServer.setHandler(wah); wah.setTempDirectory(new File("target/work")); //this allows to send large SLD's from the styles form wah.getServletContext().getContextHandler().setMaxFormContentSize(1024 * 1024 * 2); String jettyConfigFile = System.getProperty("jetty.config.file"); if (jettyConfigFile != null) { log.info("Loading Jetty config from file: " + jettyConfigFile); (new XmlConfiguration(new FileInputStream(jettyConfigFile))).configure(jettyServer); } long start = System.currentTimeMillis(); log.severe("GeoServer starting"); jettyServer.start(); long end = System.currentTimeMillis(); log.severe("GeoServer startup complete in " + (end - start) / 1000. + "s"); /* * Reads from System.in looking for the string "stop\n" in order to gracefully terminate * the jetty server and shut down the JVM. This way we can invoke the shutdown hooks * while debugging in eclipse. Can't catch CTRL-C to emulate SIGINT as the eclipse * console is not propagating that event */ Thread stopThread = new Thread() { @Override public void run() { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String line; try { while (true) { line = reader.readLine(); if ("stop".equals(line)) { jettyServer.stop(); System.exit(0); } } } catch (Exception e) { e.printStackTrace(); System.exit(1); } } }; stopThread.setDaemon(true); stopThread.run(); // use this to test normal stop behaviour, that is, to check stuff that // need to be done on container shutdown (and yes, this will make // jetty stop just after you started it...) // jettyServer.stop(); } catch (Exception e) { log.log(Level.SEVERE, "Could not start the Jetty server: " + e.getMessage(), e); if (jettyServer != null) { try { jettyServer.stop(); } catch (Exception e1) { log.log(Level.SEVERE, "Unable to stop the " + "Jetty server:" + e1.getMessage(), e1); } } } } private static SslContextFactory createSSLContextFactory(String hostname) { String password= "changeit"; File userHome = new File(System.getProperty("user.home")); File geoserverDir = new File(userHome,".geoserver"); if (!geoserverDir.exists()) { geoserverDir.mkdir(); } File keyStoreFile = new File(geoserverDir,"keystore.jks"); try { assureSelfSignedServerCertificate(hostname,keyStoreFile,password); } catch (Exception e) { log.log(Level.WARNING, "NO SSL available", e); return null; } SslContextFactory ssl = new SslContextFactory(); ssl.setKeyStorePath(keyStoreFile.getAbsolutePath()); ssl.setKeyStorePassword(password); File javaHome = new File(System.getProperty("java.home")); File cacerts = new File(javaHome, "lib").toPath().resolve("security/cacerts").toFile(); if (!cacerts.exists()) { return null; } ssl.setTrustStorePath(cacerts.getAbsolutePath()); ssl.setTrustStorePassword("changeit"); return ssl; } private static void assureSelfSignedServerCertificate(String hostname, File keyStoreFile, String password) throws Exception { KeyStore privateKS = KeyStore.getInstance("JKS"); if (keyStoreFile.exists()) { FileInputStream fis = new FileInputStream(keyStoreFile); privateKS.load(fis, password.toCharArray()); if (keyStoreContainsCertificate(privateKS, hostname)) return; } else { privateKS.load(null); } // create a RSA key pair generator using 1024 bits KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(1024); KeyPair KPair = keyPairGenerator.generateKeyPair(); // cerate a X509 certifacte generator X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator(); // set validity to 10 years, issuer and subject are equal --> self singed certificate int random = new SecureRandom().nextInt(); if (random < 0 ) random*=-1; v3CertGen.setSerialNumber(BigInteger.valueOf(random)); v3CertGen.setIssuerDN(new X509Principal("CN=" + hostname + ", OU=None, O=None L=None, C=None")); v3CertGen.setNotBefore(new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30)); v3CertGen.setNotAfter(new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 365*10))); v3CertGen.setSubjectDN(new X509Principal("CN=" + hostname + ", OU=None, O=None L=None, C=None")); v3CertGen.setPublicKey(KPair.getPublic()); v3CertGen.setSignatureAlgorithm("MD5WithRSAEncryption"); X509Certificate PKCertificate = v3CertGen.generateX509Certificate(KPair.getPrivate()); // store the certificate containing the public key,this file is needed // to import the public key in other key store. File certFile = new File(keyStoreFile.getParentFile(),hostname+".cert"); FileOutputStream fos = new FileOutputStream(certFile.getAbsoluteFile()); fos.write(PKCertificate.getEncoded()); fos.close(); privateKS.setKeyEntry(hostname+".key", KPair.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{PKCertificate}); privateKS.setCertificateEntry(hostname+".cert",PKCertificate); privateKS.store( new FileOutputStream(keyStoreFile), password.toCharArray()); } private static boolean keyStoreContainsCertificate(KeyStore ks, String hostname) throws Exception{ SubjectDnX509PrincipalExtractor ex = new SubjectDnX509PrincipalExtractor(); Enumeration<String> e = ks.aliases(); while (e.hasMoreElements()) { String alias = e.nextElement(); if (ks.isCertificateEntry(alias)) { Certificate c = ks.getCertificate(alias); if (c instanceof X509Certificate) { X500Principal p = (X500Principal) ((X509Certificate) c).getSubjectX500Principal(); if (p.getName().contains(hostname)) return true; } } } return false; } }