diff options
author | James Moger <james.moger@gitblit.com> | 2012-12-05 19:05:59 -0500 |
---|---|---|
committer | James Moger <james.moger@gitblit.com> | 2012-12-05 19:05:59 -0500 |
commit | 6db4261a984da3d70cd3ac35869f19a75edc0ce8 (patch) | |
tree | 41f724d964998c387bc535a5837b8fe8f5bc618b /src/com/gitblit | |
parent | 6c127c1ce12994ab4a235c53c254cf776e347e69 (diff) | |
download | gitblit-6db4261a984da3d70cd3ac35869f19a75edc0ce8.tar.gz gitblit-6db4261a984da3d70cd3ac35869f19a75edc0ce8.zip |
Implemented hot-reloadable CRL
Diffstat (limited to 'src/com/gitblit')
-rw-r--r-- | src/com/gitblit/GitBlitServer.java | 40 | ||||
-rw-r--r-- | src/com/gitblit/GitblitSslContextFactory.java | 94 | ||||
-rw-r--r-- | src/com/gitblit/GitblitTrustManager.java | 125 |
3 files changed, 226 insertions, 33 deletions
diff --git a/src/com/gitblit/GitBlitServer.java b/src/com/gitblit/GitBlitServer.java index 5eaa4c90..4c0e89f6 100644 --- a/src/com/gitblit/GitBlitServer.java +++ b/src/com/gitblit/GitBlitServer.java @@ -44,7 +44,6 @@ import org.eclipse.jetty.server.session.HashSessionManager; 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.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jgit.storage.file.FileBasedConfig;
@@ -426,53 +425,28 @@ public class GitBlitServer { private static Connector createSSLConnector(String certAlias, File keyStore, File clientTrustStore,
String storePassword, File caRevocationList, boolean useNIO, int port,
boolean requireClientCertificates) {
- SslContextFactory sslContext = new SslContextFactory(SslContextFactory.DEFAULT_KEYSTORE_PATH);
+ GitblitSslContextFactory factory = new GitblitSslContextFactory(certAlias,
+ keyStore, clientTrustStore, storePassword, caRevocationList);
SslConnector connector;
if (useNIO) {
logger.info("Setting up NIO SslSelectChannelConnector on port " + port);
- SslSelectChannelConnector ssl = new SslSelectChannelConnector(sslContext);
+ SslSelectChannelConnector ssl = new SslSelectChannelConnector(factory);
ssl.setSoLingerTime(-1);
if (requireClientCertificates) {
- sslContext.setNeedClientAuth(true);
+ factory.setNeedClientAuth(true);
} else {
- sslContext.setWantClientAuth(true);
+ factory.setWantClientAuth(true);
}
ssl.setThreadPool(new QueuedThreadPool(20));
connector = ssl;
} else {
logger.info("Setting up NIO SslSocketConnector on port " + port);
- SslSocketConnector ssl = new SslSocketConnector(sslContext);
+ SslSocketConnector ssl = new SslSocketConnector(factory);
connector = ssl;
}
- // disable renegotiation unless this is a patched JVM
- boolean allowRenegotiation = false;
- String v = System.getProperty("java.version");
- if (v.startsWith("1.7")) {
- allowRenegotiation = true;
- } else if (v.startsWith("1.6")) {
- // 1.6.0_22 was first release with RFC-5746 implemented fix.
- if (v.indexOf('_') > -1) {
- String b = v.substring(v.indexOf('_') + 1);
- if (Integer.parseInt(b) >= 22) {
- allowRenegotiation = true;
- }
- }
- }
- if (allowRenegotiation) {
- logger.info(" allowing SSL renegotiation on Java " + v);
- sslContext.setAllowRenegotiate(allowRenegotiation);
- }
- sslContext.setKeyStorePath(keyStore.getAbsolutePath());
- sslContext.setKeyStorePassword(storePassword);
- sslContext.setTrustStore(clientTrustStore.getAbsolutePath());
- sslContext.setTrustStorePassword(storePassword);
- sslContext.setCrlPath(caRevocationList.getAbsolutePath());
- if (!StringUtils.isEmpty(certAlias)) {
- logger.info(" certificate alias = " + certAlias);
- sslContext.setCertAlias(certAlias);
- }
connector.setPort(port);
connector.setMaxIdleTime(30000);
+
return connector;
}
diff --git a/src/com/gitblit/GitblitSslContextFactory.java b/src/com/gitblit/GitblitSslContextFactory.java new file mode 100644 index 00000000..f025c452 --- /dev/null +++ b/src/com/gitblit/GitblitSslContextFactory.java @@ -0,0 +1,94 @@ +/*
+ * Copyright 2012 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit;
+
+import java.io.File;
+import java.security.KeyStore;
+import java.security.cert.CRL;
+import java.util.Collection;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.utils.StringUtils;
+
+/**
+ * Special SSL context factory that configures Gitblit GO and replaces the
+ * primary trustmanager with a GitblitTrustManager.
+ *
+ * @author James Moger
+ */
+public class GitblitSslContextFactory extends SslContextFactory {
+
+ private static final Logger logger = LoggerFactory.getLogger(GitblitSslContextFactory.class);
+
+ private final File caRevocationList;
+
+ public GitblitSslContextFactory(String certAlias, File keyStore, File clientTrustStore,
+ String storePassword, File caRevocationList) {
+ super(keyStore.getAbsolutePath());
+
+ this.caRevocationList = caRevocationList;
+
+ // disable renegotiation unless this is a patched JVM
+ boolean allowRenegotiation = false;
+ String v = System.getProperty("java.version");
+ if (v.startsWith("1.7")) {
+ allowRenegotiation = true;
+ } else if (v.startsWith("1.6")) {
+ // 1.6.0_22 was first release with RFC-5746 implemented fix.
+ if (v.indexOf('_') > -1) {
+ String b = v.substring(v.indexOf('_') + 1);
+ if (Integer.parseInt(b) >= 22) {
+ allowRenegotiation = true;
+ }
+ }
+ }
+ if (allowRenegotiation) {
+ logger.info(" allowing SSL renegotiation on Java " + v);
+ setAllowRenegotiate(allowRenegotiation);
+ }
+
+
+ if (!StringUtils.isEmpty(certAlias)) {
+ logger.info(" certificate alias = " + certAlias);
+ setCertAlias(certAlias);
+ }
+ setKeyStorePassword(storePassword);
+ setTrustStore(clientTrustStore.getAbsolutePath());
+ setTrustStorePassword(storePassword);
+
+ logger.info(" keyStorePath = " + keyStore.getAbsolutePath());
+ logger.info(" trustStorePath = " + clientTrustStore.getAbsolutePath());
+ logger.info(" crlPath = " + caRevocationList.getAbsolutePath());
+ }
+
+ @Override
+ protected TrustManager[] getTrustManagers(KeyStore trustStore, Collection<? extends CRL> crls)
+ throws Exception {
+ TrustManager[] managers = super.getTrustManagers(trustStore, crls);
+ X509TrustManager delegate = (X509TrustManager) managers[0];
+ GitblitTrustManager root = new GitblitTrustManager(delegate, caRevocationList);
+
+ // replace first manager with the GitblitTrustManager
+ managers[0] = root;
+ return managers;
+ }
+}
diff --git a/src/com/gitblit/GitblitTrustManager.java b/src/com/gitblit/GitblitTrustManager.java new file mode 100644 index 00000000..4127caf4 --- /dev/null +++ b/src/com/gitblit/GitblitTrustManager.java @@ -0,0 +1,125 @@ +/*
+ * Copyright 2012 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509CRL;
+import java.security.cert.X509CRLEntry;
+import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.net.ssl.X509TrustManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * GitblitTrustManager is a wrapper trust manager that hot-reloads a local file
+ * CRL and enforces client certificate revocations. The GitblitTrustManager
+ * also implements fuzzy revocation enforcement in case of issuer mismatch BUT
+ * serial number match. These rejecions are specially noted in the log.
+ *
+ * @author James Moger
+ */
+public class GitblitTrustManager implements X509TrustManager {
+
+ private static final Logger logger = LoggerFactory.getLogger(GitblitTrustManager.class);
+
+ private final X509TrustManager delegate;
+ private final File caRevocationList;
+
+ private final AtomicLong lastModified = new AtomicLong(0);
+ private volatile X509CRL crl;
+
+ public GitblitTrustManager(X509TrustManager delegate, File crlFile) {
+ this.delegate = delegate;
+ this.caRevocationList = crlFile;
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ X509Certificate cert = chain[0];
+ if (isRevoked(cert)) {
+ String message = MessageFormat.format("Rejecting revoked certificate {0,number,0} for {1}",
+ cert.getSerialNumber(), cert.getSubjectDN().getName());
+ logger.warn(message);
+ throw new CertificateException(message);
+ }
+ delegate.checkClientTrusted(chain, authType);
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ delegate.checkServerTrusted(chain, authType);
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return delegate.getAcceptedIssuers();
+ }
+
+ protected boolean isRevoked(X509Certificate cert) {
+ if (!caRevocationList.exists()) {
+ return false;
+ }
+ read();
+
+ if (crl.isRevoked(cert)) {
+ // exact cert is revoked
+ return true;
+ }
+
+ X509CRLEntry entry = crl.getRevokedCertificate(cert.getSerialNumber());
+ if (entry != null) {
+ logger.warn("Certificate issuer does not match CRL issuer, but serial number has been revoked!");
+ logger.warn(" cert issuer = " + cert.getIssuerX500Principal());
+ logger.warn(" crl issuer = " + crl.getIssuerX500Principal());
+ return true;
+ }
+
+ return false;
+ }
+
+ protected synchronized void read() {
+ if (lastModified.get() == caRevocationList.lastModified()) {
+ return;
+ }
+ logger.info("Reloading CRL from " + caRevocationList.getAbsolutePath());
+ InputStream inStream = null;
+ try {
+ inStream = new FileInputStream(caRevocationList);
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ X509CRL list = (X509CRL)cf.generateCRL(inStream);
+ crl = list;
+ lastModified.set(caRevocationList.lastModified());
+ } catch (Exception e) {
+ } finally {
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+ }
+}
\ No newline at end of file |