diff --git a/htroot/ConfigBasic.html b/htroot/ConfigBasic.html
index dacc4fd97..e58dbfe6e 100644
--- a/htroot/ConfigBasic.html
+++ b/htroot/ConfigBasic.html
@@ -93,7 +93,7 @@
- with SSL (https enabled)
+ with SSL (https enabled#(withsslenabled)#:: on port #[sslport]# #(/withsslenabled)#)
#(upnp)#::
diff --git a/htroot/ConfigBasic.java b/htroot/ConfigBasic.java
index 8207e395e..7563d9857 100644
--- a/htroot/ConfigBasic.java
+++ b/htroot/ConfigBasic.java
@@ -102,7 +102,8 @@ public class ConfigBasic {
port = env.getConfigLong("port", 8090); //this allows a low port, but it will only get one, if the user edits the config himself.
ssl = env.getConfigBool("server.https", false);
}
-
+ if (ssl) prop.put("withsslenabled_sslport",env.getHttpServer().getSslPort());
+
// check if peer name already exists
final Seed oldSeed = sb.peers.lookupByName(peerName);
if (oldSeed == null &&
@@ -134,7 +135,7 @@ public class ConfigBasic {
final YaCyHttpServer theServerCore = env.getHttpServer();
env.setConfig("port", port);
env.setConfig("server.https", ssl);
-
+
// redirect the browser to the new port
reconnect = true;
diff --git a/source/net/yacy/http/Jetty8HttpServerImpl.java b/source/net/yacy/http/Jetty8HttpServerImpl.java
index 9e8557214..141f55944 100644
--- a/source/net/yacy/http/Jetty8HttpServerImpl.java
+++ b/source/net/yacy/http/Jetty8HttpServerImpl.java
@@ -24,13 +24,18 @@
package net.yacy.http;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
+import java.security.KeyStore;
import java.util.EnumSet;
import java.util.Enumeration;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
import javax.servlet.DispatcherType;
@@ -41,6 +46,7 @@ import net.yacy.http.servlets.YaCyDefaultServlet;
import net.yacy.http.servlets.YaCyProxyServlet;
import net.yacy.http.servlets.SolrServlet.Servlet404;
import net.yacy.search.Switchboard;
+import net.yacy.utils.PKCS12Tool;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
@@ -50,9 +56,11 @@ import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
/**
* class to embedded Jetty 8 http server into YaCy
@@ -60,6 +68,7 @@ import org.eclipse.jetty.servlet.ServletHolder;
public class Jetty8HttpServerImpl implements YaCyHttpServer {
private final Server server;
+ private final int sslport = 8443; // the port to use for https
/**
* @param port TCP Port to listen for http requests
@@ -71,9 +80,25 @@ public class Jetty8HttpServerImpl implements YaCyHttpServer {
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(port);
connector.setName("httpd:"+Integer.toString(port));
- //connector.setThreadPool(new QueuedThreadPool(20));
server.addConnector(connector);
-
+
+ // add ssl/https connector
+ boolean useSSL = sb.getConfigBool("server.https", false);
+ if (useSSL) {
+ final SslContextFactory sslContextFactory = new SslContextFactory();
+ final SSLContext sslContext = initSslContext(sb);
+ if (sslContext != null) {
+ sslContextFactory.setSslContext(sslContext);
+
+ SslSelectChannelConnector sslconnector = new SslSelectChannelConnector(sslContextFactory);
+ sslconnector.setPort(sslport);
+ sslconnector.setName("ssld:" + Integer.toString(sslport)); // name must start with ssl (for withSSL() to work correctly)
+
+ server.addConnector(sslconnector);
+ ConcurrentLog.info("SERVER", "SSL support initialized successfully on port " + sslport);
+ }
+ }
+
YacyDomainHandler domainHandler = new YacyDomainHandler();
domainHandler.setAlternativeResolver(sb.peers);
@@ -153,10 +178,19 @@ public class Jetty8HttpServerImpl implements YaCyHttpServer {
}
@Override
- public boolean withSSL() {
- return false; // TODO:
+ public boolean withSSL() {
+ Connector[] clist = server.getConnectors();
+ for (Connector c:clist) {
+ if (c.getName().startsWith("ssl")) return true;
+ }
+ return false;
}
+ @Override
+ public int getSslPort() {
+ return sslport;
+ }
+
/**
* reconnect with new port settings (after waiting milsec) - routine returns
* immediately
@@ -265,4 +299,104 @@ public class Jetty8HttpServerImpl implements YaCyHttpServer {
return "Jetty " + Server.getVersion();
}
+ /**
+ * Init SSL Context from config settings
+ * @param sb Switchboard
+ * @return default or sslcontext according to config
+ */
+ private SSLContext initSslContext(Switchboard sb) {
+
+ // getting the keystore file name
+ String keyStoreFileName = sb.getConfig("keyStore", "").trim();
+
+ // getting the keystore pwd
+ String keyStorePwd = sb.getConfig("keyStorePassword", "").trim();
+
+ // take a look if we have something to import
+ final String pkcs12ImportFile = sb.getConfig("pkcs12ImportFile", "").trim();
+
+ // if no keyStore and no import is defined, then set the default key
+ if (keyStoreFileName.isEmpty() && keyStorePwd.isEmpty() && pkcs12ImportFile.isEmpty()) {
+ keyStoreFileName = "defaults/freeworldKeystore";
+ keyStorePwd = "freeworld";
+ sb.setConfig("keyStore", keyStoreFileName);
+ sb.setConfig("keyStorePassword", keyStorePwd);
+ }
+
+ if (pkcs12ImportFile.length() > 0) {
+ ConcurrentLog.info("SERVER", "Import certificates from import file '" + pkcs12ImportFile + "'.");
+
+ try {
+ // getting the password
+ final String pkcs12ImportPwd = sb.getConfig("pkcs12ImportPwd", "").trim();
+
+ // creating tool to import cert
+ final PKCS12Tool pkcsTool = new PKCS12Tool(pkcs12ImportFile,pkcs12ImportPwd);
+
+ // creating a new keystore file
+ if (keyStoreFileName.isEmpty()) {
+ // using the default keystore name
+ keyStoreFileName = "DATA/SETTINGS/myPeerKeystore";
+
+ // creating an empty java keystore
+ final KeyStore ks = KeyStore.getInstance("JKS");
+ ks.load(null,keyStorePwd.toCharArray());
+ final FileOutputStream ksOut = new FileOutputStream(keyStoreFileName);
+ ks.store(ksOut, keyStorePwd.toCharArray());
+ ksOut.close();
+
+ // storing path to keystore into config file
+ sb.setConfig("keyStore", keyStoreFileName);
+ }
+
+ // importing certificate
+ pkcsTool.importToJKS(keyStoreFileName, keyStorePwd);
+
+ // removing entries from config file
+ sb.setConfig("pkcs12ImportFile", "");
+ sb.setConfig("keyStorePassword", "");
+
+ // deleting original import file
+ // TODO: should we do this
+
+ } catch (final Exception e) {
+ ConcurrentLog.severe("SERVER", "Unable to import certificate from import file '" + pkcs12ImportFile + "'.",e);
+ }
+ } else if (keyStoreFileName.isEmpty()) return null;
+
+
+ // get the ssl context
+ try {
+ ConcurrentLog.info("SERVER","Initializing SSL support ...");
+
+ // creating a new keystore instance of type (java key store)
+ if (ConcurrentLog.isFine("SERVER")) ConcurrentLog.fine("SERVER", "Initializing keystore ...");
+ final KeyStore ks = KeyStore.getInstance("JKS");
+
+ // loading keystore data from file
+ if (ConcurrentLog.isFine("SERVER")) ConcurrentLog.fine("SERVER","Loading keystore file " + keyStoreFileName);
+ final FileInputStream stream = new FileInputStream(keyStoreFileName);
+ ks.load(stream, keyStorePwd.toCharArray());
+ stream.close();
+
+ // creating a keystore factory
+ if (ConcurrentLog.isFine("SERVER")) ConcurrentLog.fine("SERVER","Initializing key manager factory ...");
+ final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ kmf.init(ks,keyStorePwd.toCharArray());
+
+ // initializing the ssl context
+ if (ConcurrentLog.isFine("SERVER")) ConcurrentLog.fine("SERVER","Initializing SSL context ...");
+ final SSLContext sslcontext = SSLContext.getInstance("TLS");
+ sslcontext.init(kmf.getKeyManagers(), null, null);
+
+ return sslcontext;
+ } catch (final Exception e) {
+ final String errorMsg = "FATAL ERROR: Unable to initialize the SSL Socket factory. " + e.getMessage();
+ ConcurrentLog.severe("SERVER",errorMsg);
+ System.out.println(errorMsg);
+ System.exit(0);
+ return null;
+ }
+ }
+
}
diff --git a/source/net/yacy/http/YaCyHttpServer.java b/source/net/yacy/http/YaCyHttpServer.java
index e869c0572..eb39e997a 100644
--- a/source/net/yacy/http/YaCyHttpServer.java
+++ b/source/net/yacy/http/YaCyHttpServer.java
@@ -23,6 +23,7 @@ public interface YaCyHttpServer {
abstract InetSocketAddress generateSocketAddress(String port) throws SocketException;
abstract int getMaxSessionCount();
abstract int getJobCount();
+ abstract int getSslPort();
abstract boolean withSSL();
abstract void reconnect(int milsec);
abstract String getVersion();
diff --git a/source/net/yacy/migration.java b/source/net/yacy/migration.java
index ddc45b602..2fff620c8 100644
--- a/source/net/yacy/migration.java
+++ b/source/net/yacy/migration.java
@@ -37,7 +37,11 @@ import net.yacy.search.Switchboard;
import net.yacy.search.SwitchboardConstants;
import com.google.common.io.Files;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import net.yacy.cora.lod.vocabulary.Tagging;
+import net.yacy.cora.protocol.TimeoutRequest;
import net.yacy.cora.storage.Configuration.Entry;
import net.yacy.cora.util.ConcurrentLog;
import net.yacy.document.LibraryProvider;
@@ -72,6 +76,16 @@ public class migration {
migrateWorkFiles(sb);
}
installSkins(sb); // FIXME: yes, bad fix for quick release 0.47
+
+ // ssl/https support currently on hardcoded default port 8443 (v1.67/9563)
+ // make sure YaCy can start (disable ssl/https support if port is used)
+ try {
+ if (TimeoutRequest.ping("127.0.0.1", 8443, 3000)) {
+ sb.setConfig("server.https", false);
+ ConcurrentLog.info("MIGRATION", "disabled https support (reason: default port 8443 already used)");
+ }
+ } catch (ExecutionException ex) {
+ }
}
/*
* remove the static defaultfiles. We use them through a overlay now.
diff --git a/source/net/yacy/search/Switchboard.java b/source/net/yacy/search/Switchboard.java
index 7cb682705..0d600f8ce 100644
--- a/source/net/yacy/search/Switchboard.java
+++ b/source/net/yacy/search/Switchboard.java
@@ -3657,7 +3657,7 @@ public final class Switchboard extends serverSwitch {
mySeed.put(Seed.UTC, GenericFormatter.UTCDiffString());
mySeed.setFlagAcceptRemoteCrawl(getConfigBool("crawlResponse", true));
mySeed.setFlagAcceptRemoteIndex(getConfigBool("allowReceiveIndex", true));
- mySeed.setFlagSSLAvailable(getConfigBool("server.https", false));
+ mySeed.setFlagSSLAvailable(this.getHttpServer().withSSL() && getConfigBool("server.https", false));
}
public void loadSeedLists() {
diff --git a/source/net/yacy/yacy.java b/source/net/yacy/yacy.java
index 15f002521..571e0691a 100644
--- a/source/net/yacy/yacy.java
+++ b/source/net/yacy/yacy.java
@@ -358,7 +358,7 @@ public final class yacy {
final String browserPopUpPage = sb.getConfig(SwitchboardConstants.BROWSER_POP_UP_PAGE, "ConfigBasic.html");
//boolean properPW = (sb.getConfig("adminAccount", "").isEmpty()) && (sb.getConfig(httpd.ADMIN_ACCOUNT_B64MD5, "").length() > 0);
//if (!properPW) browserPopUpPage = "ConfigBasic.html";
- Browser.openBrowser((false?"https":"http") + "://localhost:" + port + "/" + browserPopUpPage);
+ Browser.openBrowser((httpServer.withSSL()?"https://localhost:"+httpServer.getSslPort():"http://localhost:"+port) + "/" + browserPopUpPage);
// Browser.openBrowser((server.withSSL()?"https":"http") + "://localhost:" + serverCore.getPortNr(port) + "/" + browserPopUpPage);
} catch (final Throwable e) {
// cannot open browser. This may be normal in headless environments