private final BundleContext bc = FrameworkUtil.getBundle(JettyConfig.class).getBundleContext();
- //private static final String JETTY_PROPERTY_PREFIX = "org.eclipse.equinox.http.jetty.";
+ // private static final String JETTY_PROPERTY_PREFIX =
+ // "org.eclipse.equinox.http.jetty.";
public void start() {
// We need to start asynchronously so that Jetty bundle get started by lazy init
}
}
- int tryCount = 30;
+ long begin = System.currentTimeMillis();
+ int tryCount = 60;
try {
- tryGettyJetty: while (tryCount > 0) {
+ while (tryCount > 0) {
try {
// FIXME deal with multiple ids
JettyConfigurator.startServer(CmsConstants.DEFAULT, new Hashtable<>(config));
// Explicitly starts Jetty OSGi HTTP bundle, so that it gets triggered if OSGi
// configuration is not cleaned
FrameworkUtil.getBundle(JettyConfigurator.class).start();
- break tryGettyJetty;
+ return;
} catch (IllegalStateException e) {
// e.printStackTrace();
// Jetty may not be ready
}
tryCount--;
}
+ long duration = System.currentTimeMillis() - begin;
+ log.error("Gave up with starting Jetty server after " + (duration / 1000) + " s");
}
} catch (Exception e) {
log.error("Cannot start default Jetty server with config " + properties, e);
if (httpHost != null)
props.put(JettyHttpConstants.HTTPS_HOST, httpHost);
- props.put(JettyHttpConstants.SSL_KEYSTORETYPE, getFrameworkProp(CmsDeployProperty.SSL_KEYSTORETYPE));
+ // keystore
+ props.put(JettyHttpConstants.SSL_KEYSTORETYPE, getFrameworkProp(CmsDeployProperty.SSL_KEYSTORETYPE));
props.put(JettyHttpConstants.SSL_KEYSTORE, getFrameworkProp(CmsDeployProperty.SSL_KEYSTORE));
props.put(JettyHttpConstants.SSL_PASSWORD, getFrameworkProp(CmsDeployProperty.SSL_PASSWORD));
+ // truststore
+ props.put(JettyHttpConstants.SSL_TRUSTSTORETYPE,
+ getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTORETYPE));
+ props.put(JettyHttpConstants.SSL_TRUSTSTORE, getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTORE));
+ props.put(JettyHttpConstants.SSL_TRUSTSTOREPASSWORD,
+ getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTOREPASSWORD));
+
// client certificate authentication
String wantClientAuth = getFrameworkProp(CmsDeployProperty.SSL_WANTCLIENTAUTH);
if (wantClientAuth != null)
static final String SSL_PROTOCOL = "ssl.protocol";
static final String SSL_ALGORITHM = "ssl.algorithm";
static final String SSL_KEYSTORETYPE = "ssl.keystoretype";
+
+ // Argeo
+ static final String SSL_TRUSTSTORE = "ssl.truststore";
+ static final String SSL_TRUSTSTOREPASSWORD = "ssl.truststorepassword";
+ static final String SSL_TRUSTSTORETYPE = "ssl.truststoretype";
+
}
import javax.websocket.server.ServerContainer;
import org.eclipse.equinox.http.jetty.JettyCustomizer;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator;
import org.osgi.framework.BundleContext;
/** Customises the Jetty HTTP server. */
public class CmsJettyCustomizer extends JettyCustomizer {
+ static final String SSL_TRUSTSTORE = "ssl.truststore";
+ static final String SSL_TRUSTSTOREPASSWORD = "ssl.truststorepassword";
+ static final String SSL_TRUSTSTORETYPE = "ssl.truststoretype";
+
private BundleContext bc = FrameworkUtil.getBundle(CmsJettyCustomizer.class).getBundleContext();
public final static String WEBSOCKET_ENABLED = "argeo.websocket.enabled";
return super.customizeContext(context, settings);
}
+
+ @Override
+ public Object customizeHttpsConnector(Object connector, Dictionary<String, ?> settings) {
+ ServerConnector httpsConnector = (ServerConnector) connector;
+ for (ConnectionFactory connectionFactory : httpsConnector.getConnectionFactories()) {
+ if (connectionFactory instanceof SslConnectionFactory) {
+ SslContextFactory.Server sslConnectionFactory = ((SslConnectionFactory) connectionFactory)
+ .getSslContextFactory();
+ sslConnectionFactory.setTrustStorePath((String) settings.get(SSL_TRUSTSTORE));
+ sslConnectionFactory.setTrustStoreType((String) settings.get(SSL_TRUSTSTORETYPE));
+ sslConnectionFactory.setTrustStorePassword((String) settings.get(SSL_TRUSTSTOREPASSWORD));
+ }
+ }
+ return super.customizeHttpsConnector(connector, settings);
+ }
+
}
SSL_PROTOCOL("argeo.ssl.protocol"),
/** SSL algorithm to use. */
SSL_ALGORITHM("argeo.ssl.algorithm"),
+ /** Custom SSL trust store. */
+ SSL_TRUSTSTORE("argeo.ssl.truststore"),
+ /** Custom SSL trust store type. */
+ SSL_TRUSTSTORETYPE("argeo.ssl.truststoretype"),
+ /** Custom SSL trust store type. */
+ SSL_TRUSTSTOREPASSWORD("argeo.ssl.truststorepassword"),
//
// WEBSOCKET
//
}
private static HttpClient openHttpClient(Subject subject) {
- // disable https check
- // jdk.internal.httpclient.disableHostnameVerification=true
- HttpClient client = HttpClient.newBuilder().sslContext(insecureContext())
-// .authenticator(new Authenticator() {
-// public PasswordAuthentication getPasswordAuthentication() {
-// return null;
-// }
-//
-// })
- .version(HttpClient.Version.HTTP_1_1).build();
+ HttpClient client = HttpClient.newBuilder() //
+// .sslContext(insecureContext()) //
+ .version(HttpClient.Version.HTTP_1_1) //
+ .build();
return client;
-
- // return client;
-// AuthPolicy.registerAuthScheme(SpnegoAuthScheme.NAME, SpnegoAuthScheme.class);
-// HttpParams params = DefaultHttpParams.getDefaultParams();
-// ArrayList<String> schemes = new ArrayList<>();
-// schemes.add(SpnegoAuthScheme.NAME);
-// params.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, schemes);
-// params.setParameter(CredentialsProvider.PROVIDER, new HttpCredentialProvider());
-// HttpClient httpClient = new HttpClient();
-// httpClient.executeMethod(new GetMethod(("https://" + server + "/ipa/session/json")));
-// return httpClient;
-
}
- private static SSLContext insecureContext() {
+ static SSLContext insecureContext() {
TrustManager[] noopTrustManager = new TrustManager[] { new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs, String string) {
}
deployPropertyDefaults.put(CmsDeployProperty.NODE_INIT, "../../init");
deployPropertyDefaults.put(CmsDeployProperty.LOCALE, Locale.getDefault().toString());
+ // certificates
deployPropertyDefaults.put(CmsDeployProperty.SSL_KEYSTORETYPE, PkiUtils.PKCS12);
- deployPropertyDefaults.put(CmsDeployProperty.SSL_PASSWORD, "changeit");
+ deployPropertyDefaults.put(CmsDeployProperty.SSL_PASSWORD, PkiUtils.DEFAULT_KEYSTORE_PASSWORD);
Path keyStorePath = getDataPath(PkiUtils.DEFAULT_KEYSTORE_PATH);
deployPropertyDefaults.put(CmsDeployProperty.SSL_KEYSTORE, keyStorePath.toAbsolutePath().toString());
+ Path trustStorePath = getDataPath(PkiUtils.DEFAULT_TRUSTSTORE_PATH);
+ deployPropertyDefaults.put(CmsDeployProperty.SSL_TRUSTSTORETYPE, PkiUtils.PKCS12);
+ deployPropertyDefaults.put(CmsDeployProperty.SSL_TRUSTSTOREPASSWORD, PkiUtils.DEFAULT_KEYSTORE_PASSWORD);
+ deployPropertyDefaults.put(CmsDeployProperty.SSL_TRUSTSTORE, trustStorePath.toAbsolutePath().toString());
+
this.deployPropertyDefaults = Collections.unmodifiableMap(deployPropertyDefaults);
}
Path pemCertPath = getDataPath(PkiUtils.DEFAULT_PEM_CERT_PATH);
char[] keyStorePassword = getDeployProperty(CmsDeployProperty.SSL_PASSWORD).toCharArray();
+ // Keystore
// if PEM files both exists, update the PKCS12 file
if (Files.exists(pemCertPath) && Files.exists(pemKeyPath)) {
// TODO check certificate update time? monitor changes?
- KeyStore keyStore = PkiUtils.getKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12);
+ KeyStore keyStore = PkiUtils.getKeyStore(keyStorePath, keyStorePassword,
+ getDeployProperty(CmsDeployProperty.SSL_KEYSTORETYPE));
try (Reader key = Files.newBufferedReader(pemKeyPath, StandardCharsets.US_ASCII);
Reader cert = Files.newBufferedReader(pemCertPath, StandardCharsets.US_ASCII);) {
PkiUtils.loadPem(keyStore, key, keyStorePassword, cert);
}
}
+ // Truststore
+ Path trustStorePath = Paths.get(getDeployProperty(CmsDeployProperty.SSL_TRUSTSTORE));
+ char[] trustStorePassword = getDeployProperty(CmsDeployProperty.SSL_TRUSTSTOREPASSWORD).toCharArray();
+
+ // IPA CA
+ Path ipaCaCertPath = Paths.get(PkiUtils.IPA_PEM_CA_CERT_PATH);
+ if (Files.exists(ipaCaCertPath)) {
+ KeyStore trustStore = PkiUtils.getKeyStore(trustStorePath, trustStorePassword,
+ getDeployProperty(CmsDeployProperty.SSL_TRUSTSTORETYPE));
+ try (Reader cert = Files.newBufferedReader(ipaCaCertPath, StandardCharsets.US_ASCII);) {
+ PkiUtils.loadPem(trustStore, null, trustStorePassword, cert);
+ PkiUtils.saveKeyStore(trustStorePath, trustStorePassword, trustStore);
+ if (log.isDebugEnabled())
+ log.debug("IPA CA certificate stored in " + trustStorePath);
+ } catch (IOException e) {
+ log.error("Cannot trust CA certificate", e);
+ }
+ }
+
if (!Files.exists(keyStorePath))
PkiUtils.createSelfSignedKeyStore(keyStorePath, keyStorePassword, PkiUtils.PKCS12);
// props.put(JettyHttpConstants.SSL_KEYSTORETYPE, PkiUtils.PKCS12);
// try defaults
if (deployPropertyDefaults.containsKey(deployProperty)) {
value = deployPropertyDefaults.get(deployProperty);
+ if (deployProperty.isSystemPropertyOnly())
+ System.setProperty(deployProperty.getProperty(), value);
}
- // try legacy properties
- String legacyProperty = switch (deployProperty) {
- case DIRECTORY -> "argeo.node.useradmin.uris";
- case DB_URL -> "argeo.node.dburl";
- case DB_USER -> "argeo.node.dbuser";
- case DB_PASSWORD -> "argeo.node.dbpassword";
- case HTTP_PORT -> "org.osgi.service.http.port";
- case HTTPS_PORT -> "org.osgi.service.http.port.secure";
- case HOST -> "org.eclipse.equinox.http.jetty.http.host";
- case LOCALE -> "argeo.i18n.defaultLocale";
-
- default -> null;
- };
- if (legacyProperty != null) {
- value = doGetDeployProperty(legacyProperty);
- if (value != null) {
- log.warn("Retrieved deploy property " + deployProperty.getProperty()
- + " through deprecated property " + legacyProperty);
+
+ if (value == null) {
+ // try legacy properties
+ String legacyProperty = switch (deployProperty) {
+ case DIRECTORY -> "argeo.node.useradmin.uris";
+ case DB_URL -> "argeo.node.dburl";
+ case DB_USER -> "argeo.node.dbuser";
+ case DB_PASSWORD -> "argeo.node.dbpassword";
+ case HTTP_PORT -> "org.osgi.service.http.port";
+ case HTTPS_PORT -> "org.osgi.service.http.port.secure";
+ case HOST -> "org.eclipse.equinox.http.jetty.http.host";
+ case LOCALE -> "argeo.i18n.defaultLocale";
+
+ default -> null;
+ };
+ if (legacyProperty != null) {
+ value = doGetDeployProperty(legacyProperty);
+ if (value != null) {
+ log.warn("Retrieved deploy property " + deployProperty.getProperty()
+ + " through deprecated property " + legacyProperty);
+ }
}
}
}
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
+import java.security.KeyStore.TrustedCertificateEntry;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.SecureRandom;
class PkiUtils {
private final static CmsLog log = CmsLog.getLog(PkiUtils.class);
- public final static String PKCS12 = "PKCS12";
- public static final String DEFAULT_KEYSTORE_PATH = KernelConstants.DIR_NODE + '/' + CmsConstants.NODE + ".p12";
+ final static String PKCS12 = "PKCS12";
+ final static String JKS = "JKS";
- public static final String DEFAULT_PEM_KEY_PATH = KernelConstants.DIR_NODE + '/' + CmsConstants.NODE + ".key";
+ static final String DEFAULT_KEYSTORE_PATH = KernelConstants.DIR_NODE + '/' + CmsConstants.NODE + ".p12";
- public static final String DEFAULT_PEM_CERT_PATH = KernelConstants.DIR_NODE + '/' + CmsConstants.NODE + ".crt";
+ static final String DEFAULT_TRUSTSTORE_PATH = KernelConstants.DIR_NODE + "/trusted.p12";
+
+ static final String DEFAULT_PEM_KEY_PATH = KernelConstants.DIR_NODE + '/' + CmsConstants.NODE + ".key";
+
+ static final String DEFAULT_PEM_CERT_PATH = KernelConstants.DIR_NODE + '/' + CmsConstants.NODE + ".crt";
+
+ static final String IPA_PEM_CA_CERT_PATH = "/etc/ipa/ca.crt";
+
+ static final String DEFAULT_KEYSTORE_PASSWORD = "changeit";
private final static String SECURITY_PROVIDER;
+ private final static String BC_PROVIDER;
static {
Security.addProvider(new BouncyCastleProvider());
- SECURITY_PROVIDER = "BC";
+ // BouncyCastle does not store trusted certificates properly
+ // TODO report it
+ BC_PROVIDER = "BC";
+ SECURITY_PROVIDER = "SUN";
}
public static X509Certificate generateSelfSignedCertificate(KeyStore keyStore, X500Principal x500Principal,
public static KeyStore getKeyStore(Path keyStoreFile, char[] keyStorePassword, String keyStoreType) {
try {
- KeyStore store = KeyStore.getInstance(keyStoreType, SECURITY_PROVIDER);
+ KeyStore store = KeyStore.getInstance(keyStoreType, "SunJSSE");
if (Files.exists(keyStoreFile)) {
try (InputStream fis = Files.newInputStream(keyStoreFile)) {
store.load(fis, keyStorePassword);
// }
public static void loadPem(KeyStore keyStore, Reader key, char[] keyPassword, Reader cert) {
- PrivateKey privateKey = loadPemPrivateKey(key, keyPassword);
- X509Certificate certificate = loadPemCertificate(cert);
try {
- keyStore.setKeyEntry(certificate.getSubjectX500Principal().getName(), privateKey, keyPassword,
- new java.security.cert.Certificate[] { certificate });
+ X509Certificate certificate = loadPemCertificate(cert);
+ if (key != null) {
+ PrivateKey privateKey = loadPemPrivateKey(key, keyPassword);
+ keyStore.setKeyEntry(certificate.getSubjectX500Principal().getName(), privateKey, keyPassword,
+ new java.security.cert.Certificate[] { certificate });
+ } else {
+ TrustedCertificateEntry trustedCertificateEntry = new TrustedCertificateEntry(certificate);
+ keyStore.setEntry(certificate.getSubjectX500Principal().getName(), trustedCertificateEntry, null);
+ }
} catch (KeyStoreException e) {
throw new RuntimeException("Cannot store PEM certificate", e);
}
public static PrivateKey loadPemPrivateKey(Reader reader, char[] keyPassword) {
try (PEMParser pemParser = new PEMParser(reader)) {
- JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
+ JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BC_PROVIDER);
Object object = pemParser.readObject();
PrivateKeyInfo privateKeyInfo;
if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
Hashtable<String, Object> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
if (!dnsServerUrls.isEmpty()) {
+ boolean specified = false;
StringJoiner providerUrl = new StringJoiner(" ");
for (String dnsUrl : dnsServerUrls) {
- if (dnsUrl != null)
+ if (dnsUrl != null) {
providerUrl.add(dnsUrl);
+ specified = true;
+ }
}
- env.put(Context.PROVIDER_URL, providerUrl.toString());
+ if (specified)
+ env.put(Context.PROVIDER_URL, providerUrl.toString());
}
initialCtx = new InitialDirContext(env);
}