@@ -25,7 +25,10 @@ | |||
/build-demo.xml | |||
/users.conf | |||
*.directory | |||
/.gradle | |||
/projects.conf | |||
/pom.xml | |||
/deploy | |||
/.gradle | |||
/projects.conf | |||
/pom.xml | |||
/deploy | |||
/*.jks | |||
/x509test | |||
/certs |
@@ -111,6 +111,14 @@ | |||
<include name="users.conf" /> | |||
</fileset> | |||
</copy> | |||
<!-- copy required distribution files to project folder --> | |||
<mkdir dir="${basedir}/certs" /> | |||
<copy todir="${basedir}/certs" overwrite="false"> | |||
<fileset dir="${basedir}/distrib"> | |||
<include name="authority.conf" /> | |||
</fileset> | |||
</copy> | |||
<!-- copy gitblit.properties to the WEB-INF folder. | |||
this file is only used for parsing setting descriptions. --> | |||
@@ -174,10 +182,20 @@ | |||
<include name="**/*" /> | |||
<exclude name="federation.properties" /> | |||
<exclude name="openshift.mkd" /> | |||
<exclude name="authority.conf" /> | |||
</fileset> | |||
<fileset dir="${basedir}"> | |||
<include name="LICENSE" /> | |||
<include name="NOTICE" /> | |||
<include name="authority*.jar" /> | |||
</fileset> | |||
</copy> | |||
<!-- Certificate templates --> | |||
<mkdir dir="${project.deploy.dir}/certs"/> | |||
<copy todir="${project.deploy.dir}/certs"> | |||
<fileset dir="${basedir}/distrib"> | |||
<include name="authority.conf" /> | |||
</fileset> | |||
</copy> | |||
@@ -0,0 +1,6 @@ | |||
[new] | |||
duration = 365 | |||
organizationalUnit = Gitblit | |||
organization = Gitblit | |||
locality = Gitblit | |||
stateProvince = NY |
@@ -1137,6 +1137,20 @@ server.ajpBindInterface = localhost | |||
# RESTART REQUIRED | |||
server.storePassword = gitblit | |||
# If serving over https (recommended) you might consider requiring clients to | |||
# authenticate with ssl certificates. If enabled, only https clients with the | |||
# a valid client certificate will be able to access Gitblit. | |||
# | |||
# If disabled, client certificate authentication is optional and will be tried | |||
# first before falling-back to form authentication or basic authentication. | |||
# | |||
# Requiring client certificates to access any of Gitblit may be too extreme, | |||
# consider this carefully. | |||
# | |||
# SINCE 1.2.0 | |||
# RESTART REQUIRED | |||
server.requireClientCertificates = false | |||
# Port for shutdown monitor to listen on. | |||
# | |||
# SINCE 0.5.0 |
@@ -2,5 +2,5 @@ | |||
@REM Set HOSTNAME to the server's hostname | |||
@REM -------------------------------------------------------------------------- | |||
@SET HOSTNAME=localhost | |||
@del keystore | |||
@del serverKeyStore.jks | |||
@java -cp gitblit.jar;"%CD%\ext\*" com.gitblit.MakeCertificate --hostname %HOSTNAME% --subject "CN=%HOSTNAME%, OU=Gitblit, O=Gitblit, L=Some Town, ST=Some State, C=US" |
@@ -2,5 +2,5 @@ | |||
@REM Set HOSTNAME to the server's hostname | |||
@REM -------------------------------------------------------------------------- | |||
@SET HOSTNAME=localhost | |||
@del keystore | |||
@keytool -keystore keystore -alias %HOSTNAME% -genkey -keyalg RSA -dname "CN=%HOSTNAME%, OU=Gitblit, O=Gitblit, L=Some Town, ST=Some State, C=US" | |||
@del serverKeyStore.jks | |||
@keytool -keystore serverKeyStore.jks -alias %HOSTNAME% -genkey -keyalg RSA -dname "CN=%HOSTNAME%, OU=Gitblit, O=Gitblit, L=Some Town, ST=Some State, C=US" |
@@ -52,6 +52,10 @@ You can require all git servlet access be authenticated by a client certificate. | |||
**New:** *git.requireClientCertificates = false* | |||
**New:** *git.enforceCertificateValidity = true* | |||
**New:** *git.certificateUsernameOIDs = CN* | |||
- Revised clean install certificate generation to create a Gitblit GO Certificate Authority certificate; an SSL certificate signed by the CA certificate; and to create distinct server key and server trust stores. <u>The store files have been renamed!</u> | |||
- Added support for Gitblit GO to require usage of client certificates to access the entire server. | |||
This is extreme and should be considered carefully since it affects every https access. The default is to **want** client certificates. Setting this value to *true* changes that to **need** client certificates. | |||
**New:** *server.requireClientCertificates = false* | |||
- Added setting to control length of shortened commit ids | |||
**New:** *web.shortCommitIdLength=8* | |||
- Added simple project pages. A project is a subfolder off the *git.repositoriesFolder*. |
@@ -29,6 +29,7 @@ import java.net.UnknownHostException; | |||
import java.security.ProtectionDomain; | |||
import java.text.MessageFormat; | |||
import java.util.ArrayList; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Scanner; | |||
@@ -41,8 +42,11 @@ 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; | |||
import org.eclipse.jgit.util.FS; | |||
import org.eclipse.jgit.util.FileUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
@@ -51,7 +55,11 @@ import com.beust.jcommander.JCommander; | |||
import com.beust.jcommander.Parameter; | |||
import com.beust.jcommander.ParameterException; | |||
import com.beust.jcommander.Parameters; | |||
import com.gitblit.authority.NewCertificateConfig; | |||
import com.gitblit.utils.StringUtils; | |||
import com.gitblit.utils.TimeUtils; | |||
import com.gitblit.utils.X509Utils; | |||
import com.gitblit.utils.X509Utils.X509Metadata; | |||
import com.unboundid.ldap.listener.InMemoryDirectoryServer; | |||
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; | |||
import com.unboundid.ldap.listener.InMemoryListenerConfig; | |||
@@ -186,15 +194,32 @@ public class GitBlitServer { | |||
// conditionally configure the https connector | |||
if (params.securePort > 0) { | |||
File keystore = new File("keystore"); | |||
if (!keystore.exists()) { | |||
logger.info("Generating self-signed SSL certificate for localhost"); | |||
MakeCertificate.generateSelfSignedCertificate("localhost", keystore, | |||
params.storePassword); | |||
File folder = new File(System.getProperty("user.dir")); | |||
File certificatesConf = new File(folder, X509Utils.CA_CONFIG); | |||
File serverKeyStore = new File(folder, X509Utils.SERVER_KEY_STORE); | |||
File serverTrustStore = new File(folder, X509Utils.SERVER_TRUST_STORE); | |||
File caRevocationList = new File(folder, X509Utils.CA_REVOCATION_LIST); | |||
// generate CA & web certificates, create certificate stores | |||
X509Metadata metadata = new X509Metadata("localhost", params.storePassword); | |||
// set default certificate values from config file | |||
if (certificatesConf.exists()) { | |||
FileBasedConfig config = new FileBasedConfig(certificatesConf, FS.detect()); | |||
try { | |||
config.load(); | |||
} catch (Exception e) { | |||
logger.error("Error parsing " + certificatesConf, e); | |||
} | |||
NewCertificateConfig certificateConfig = NewCertificateConfig.KEY.parse(config); | |||
certificateConfig.update(metadata); | |||
} | |||
if (keystore.exists()) { | |||
Connector secureConnector = createSSLConnector(keystore, params.storePassword, | |||
params.useNIO, params.securePort); | |||
metadata.notAfter = new Date(System.currentTimeMillis() + 10*TimeUtils.ONEYEAR); | |||
X509Utils.prepareX509Infrastructure(metadata, folder); | |||
if (serverKeyStore.exists()) { | |||
Connector secureConnector = createSSLConnector(serverKeyStore, serverTrustStore, params.storePassword, | |||
caRevocationList, params.useNIO, params.securePort, params.requireClientCertificates); | |||
String bindInterface = settings.getString(Keys.server.httpsBindInterface, null); | |||
if (!StringUtils.isEmpty(bindInterface)) { | |||
logger.warn(MessageFormat.format( | |||
@@ -364,24 +389,34 @@ public class GitBlitServer { | |||
* 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 keystore | |||
* @param password | |||
* @param keyStore | |||
* @param clientTrustStore | |||
* @param storePassword | |||
* @param caRevocationList | |||
* @param useNIO | |||
* @param port | |||
* @param requireClientCertificates | |||
* @return an https connector | |||
*/ | |||
private static Connector createSSLConnector(File keystore, String password, boolean useNIO, | |||
int port) { | |||
private static Connector createSSLConnector(File keyStore, File clientTrustStore, | |||
String storePassword, File caRevocationList, boolean useNIO, int port, | |||
boolean requireClientCertificates) { | |||
SslContextFactory sslContext = new SslContextFactory(SslContextFactory.DEFAULT_KEYSTORE_PATH); | |||
SslConnector connector; | |||
if (useNIO) { | |||
logger.info("Setting up NIO SslSelectChannelConnector on port " + port); | |||
SslSelectChannelConnector ssl = new SslSelectChannelConnector(); | |||
SslSelectChannelConnector ssl = new SslSelectChannelConnector(sslContext); | |||
ssl.setSoLingerTime(-1); | |||
if (requireClientCertificates) { | |||
sslContext.setNeedClientAuth(true); | |||
} else { | |||
sslContext.setWantClientAuth(true); | |||
} | |||
ssl.setThreadPool(new QueuedThreadPool(20)); | |||
connector = ssl; | |||
} else { | |||
logger.info("Setting up NIO SslSocketConnector on port " + port); | |||
SslSocketConnector ssl = new SslSocketConnector(); | |||
SslSocketConnector ssl = new SslSocketConnector(sslContext); | |||
connector = ssl; | |||
} | |||
// disable renegotiation unless this is a patched JVM | |||
@@ -400,10 +435,13 @@ public class GitBlitServer { | |||
} | |||
if (allowRenegotiation) { | |||
logger.info(" allowing SSL renegotiation on Java " + v); | |||
connector.setAllowRenegotiate(allowRenegotiation); | |||
sslContext.setAllowRenegotiate(allowRenegotiation); | |||
} | |||
connector.setKeystore(keystore.getAbsolutePath()); | |||
connector.setPassword(password); | |||
sslContext.setKeyStorePath(keyStore.getAbsolutePath()); | |||
sslContext.setKeyStorePassword(storePassword); | |||
sslContext.setTrustStore(clientTrustStore.getAbsolutePath()); | |||
sslContext.setTrustStorePassword(storePassword); | |||
sslContext.setCrlPath(caRevocationList.getAbsolutePath()); | |||
connector.setPort(port); | |||
connector.setMaxIdleTime(30000); | |||
return connector; | |||
@@ -540,6 +578,9 @@ public class GitBlitServer { | |||
@Parameter(names = "--shutdownPort", description = "Port for Shutdown Monitor to listen on. (port <= 0 will disable this monitor)") | |||
public Integer shutdownPort = FILESETTINGS.getInteger(Keys.server.shutdownPort, 8081); | |||
@Parameter(names = "--requireClientCertificates", description = "Require client X509 certificates for https connections.") | |||
public Boolean requireClientCertificates = FILESETTINGS.getBoolean(Keys.server.requireClientCertificates, false); | |||
/* | |||
* Setting overrides | |||
*/ |
@@ -29,8 +29,6 @@ import java.util.Date; | |||
import javax.security.auth.x500.X500Principal; | |||
import org.bouncycastle.asn1.x500.X500NameBuilder; | |||
import org.bouncycastle.asn1.x500.style.BCStyle; | |||
import org.bouncycastle.cert.X509v3CertificateBuilder; | |||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; | |||
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; | |||
@@ -62,65 +60,17 @@ public class MakeCertificate { | |||
System.err.println(t.getMessage()); | |||
jc.usage(); | |||
} | |||
File keystore = new File("keystore"); | |||
File keystore = new File("serverKeyStore.jks"); | |||
generateSelfSignedCertificate(params.hostname, keystore, params.storePassword, | |||
params.subject); | |||
} | |||
public static void generateSelfSignedCertificate(String hostname, File keystore, | |||
String keystorePassword) { | |||
try { | |||
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); | |||
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC"); | |||
kpGen.initialize(1024, new SecureRandom()); | |||
KeyPair pair = kpGen.generateKeyPair(); | |||
// Generate self-signed certificate | |||
X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE); | |||
builder.addRDN(BCStyle.OU, Constants.NAME); | |||
builder.addRDN(BCStyle.O, Constants.NAME); | |||
builder.addRDN(BCStyle.CN, hostname); | |||
Date notBefore = new Date(System.currentTimeMillis() - TimeUtils.ONEDAY); | |||
Date notAfter = new Date(System.currentTimeMillis() + 10 * TimeUtils.ONEYEAR); | |||
BigInteger serial = BigInteger.valueOf(System.currentTimeMillis()); | |||
X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), | |||
serial, notBefore, notAfter, builder.build(), pair.getPublic()); | |||
ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption") | |||
.setProvider(BC).build(pair.getPrivate()); | |||
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC) | |||
.getCertificate(certGen.build(sigGen)); | |||
cert.checkValidity(new Date()); | |||
cert.verify(cert.getPublicKey()); | |||
// Save to keystore | |||
KeyStore store = KeyStore.getInstance("JKS"); | |||
if (keystore.exists()) { | |||
FileInputStream fis = new FileInputStream(keystore); | |||
store.load(fis, keystorePassword.toCharArray()); | |||
fis.close(); | |||
} else { | |||
store.load(null); | |||
} | |||
store.setKeyEntry(hostname, pair.getPrivate(), keystorePassword.toCharArray(), | |||
new java.security.cert.Certificate[] { cert }); | |||
FileOutputStream fos = new FileOutputStream(keystore); | |||
store.store(fos, keystorePassword.toCharArray()); | |||
fos.close(); | |||
} catch (Throwable t) { | |||
t.printStackTrace(); | |||
throw new RuntimeException("Failed to generate self-signed certificate!", t); | |||
} | |||
} | |||
public static void generateSelfSignedCertificate(String hostname, File keystore, | |||
String keystorePassword, String info) { | |||
try { | |||
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); | |||
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC"); | |||
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", BC); | |||
kpGen.initialize(1024, new SecureRandom()); | |||
KeyPair pair = kpGen.generateKeyPair(); | |||