Configure signed push globally in Gerrit config
Instead of depending on the standard C git option receive.certNonceSeed to enable signed push, require a separate option receive.enableSignedPush. This avoids tying enabling the functionality to a particular implementation of nonce validation, and in the common case we can just use a new random seed on startup. The standard C git keys receive.certNonceSeed and receive.certNonceSlop are supported, but read them from the global Gerrit config (preferably secure.config). Change-Id: Ib38d16052e8896e860e4d9639c62b2023784ef97
This commit is contained in:
@@ -2771,6 +2771,40 @@ behavior of Gerrit's 'receive-pack' mechanism.
|
||||
maxObjectSizeLimit = 40 m
|
||||
----
|
||||
|
||||
[[receive.enableSignedPush]]receive.enableSignedPush::
|
||||
+
|
||||
If true, server-side signed push validation is enabled.
|
||||
+
|
||||
When a client pushes with `git push --signed`, this ensures that the
|
||||
push certificate is valid and signed with a valid public key stored in
|
||||
the `refs/gpg-keys` branch of `All-Users`.
|
||||
+
|
||||
Defaults to false.
|
||||
|
||||
[[receive.certNonceSeed]]receive.certNonceSeed::
|
||||
+
|
||||
If set to a non-empty value and server-side signed push validation is
|
||||
link:#receive.enableSignedPush[enabled], use this value as the seed to
|
||||
the HMAC SHA-1 nonce generator. If unset, a 64-byte random seed will be
|
||||
generated at server startup.
|
||||
+
|
||||
As this is used as the seed of a cryptographic algorithm, it is
|
||||
recommended to be placed in link:#secure-config[`secure.config`].
|
||||
+
|
||||
Defaults to unset.
|
||||
|
||||
[[receive.certNonceSlop]]receive.certNonceSlop::
|
||||
+
|
||||
When validating the nonce passed as part of the signed push protocol,
|
||||
accept valid nonces up to this many seconds old. This allows
|
||||
certificate verification to work over HTTP where there is a lag between
|
||||
the HTTP response providing the nonce to sign and the next request
|
||||
containing the signed nonce. This can be significant on large
|
||||
repositories, since the lag also includes the time to count objects on
|
||||
the client.
|
||||
+
|
||||
Default is 5 minutes.
|
||||
|
||||
[[receive.checkMagicRefs]]receive.checkMagicRefs::
|
||||
+
|
||||
If true, Gerrit will verify the destination repository has
|
||||
@@ -3640,7 +3674,7 @@ notifications if the full name of the user is not set.
|
||||
By default "Anonymous Coward" is used.
|
||||
|
||||
|
||||
== File `etc/secure.config`
|
||||
== [[secure.config]]File `etc/secure.config`
|
||||
The optional file `'$site_path'/etc/secure.config` overrides (or
|
||||
supplements) the settings supplied by `'$site_path'/etc/gerrit.config`.
|
||||
The file should be readable only by the daemon process and can be
|
||||
|
||||
@@ -14,23 +14,35 @@
|
||||
|
||||
package com.google.gerrit.server.git;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.util.BouncyCastleUtil;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.transport.PreReceiveHookChain;
|
||||
import org.eclipse.jgit.transport.ReceivePack;
|
||||
import org.eclipse.jgit.transport.SignedPushConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
|
||||
public class SignedPushModule extends AbstractModule {
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(SignedPushModule.class);
|
||||
|
||||
public static boolean isEnabled(Config cfg) {
|
||||
return cfg.getBoolean("receive", null, "enableSignedPush", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
if (BouncyCastleUtil.havePGP()) {
|
||||
@@ -44,17 +56,50 @@ public class SignedPushModule extends AbstractModule {
|
||||
|
||||
@Singleton
|
||||
private static class Initializer implements ReceivePackInitializer {
|
||||
private final SignedPushConfig signedPushConfig;
|
||||
private final SignedPushPreReceiveHook hook;
|
||||
|
||||
@Inject
|
||||
Initializer(SignedPushPreReceiveHook hook) {
|
||||
Initializer(@GerritServerConfig Config cfg,
|
||||
SignedPushPreReceiveHook hook) {
|
||||
this.hook = hook;
|
||||
|
||||
if (isEnabled(cfg)) {
|
||||
String seed = cfg.getString("receive", null, "certNonceSeed");
|
||||
if (Strings.isNullOrEmpty(seed)) {
|
||||
seed = randomString(64);
|
||||
}
|
||||
signedPushConfig = new SignedPushConfig();
|
||||
signedPushConfig.setCertNonceSeed(seed);
|
||||
signedPushConfig.setCertNonceSlopLimit(
|
||||
cfg.getInt("receive", null, "certNonceSlop", 5 * 60));
|
||||
} else {
|
||||
signedPushConfig = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Project.NameKey project, ReceivePack rp) {
|
||||
rp.setSignedPushConfig(signedPushConfig);
|
||||
if (signedPushConfig != null) {
|
||||
rp.setPreReceiveHook(PreReceiveHookChain.newChain(Lists.newArrayList(
|
||||
hook, rp.getPreReceiveHook())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String randomString(int len) {
|
||||
Random random;
|
||||
try {
|
||||
random = SecureRandom.getInstance("SHA1PRNG");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
sb.append((char) random.nextInt());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -94,6 +94,8 @@ public class InMemoryModule extends FactoryModule {
|
||||
cfg.setInt("index", "lucene", "testVersion",
|
||||
ChangeSchemas.getLatest().getVersion());
|
||||
cfg.setInt("sendemail", null, "threadPoolSize", 0);
|
||||
cfg.setBoolean("receive", null, "enableSignedPush", false);
|
||||
cfg.setString("receive", null, "certNonceSeed", "sekret");
|
||||
}
|
||||
|
||||
private final Config cfg;
|
||||
|
||||
Reference in New Issue
Block a user