Option to skip library download during init

The init tool offers a mechanism to download a required library like
BouncyCastle during installation, and to remove "stale" versions of
that library from the lib/ folder if specified in libraries.config.
However, init does not check (and in general cannot check) that
the library actually is stale, but forcefully replaces it with the
version stated in libraries.config.

For security critical libraries like BouncyCastle this is dangerous,
especially when doing a batch install. In that case, init may silently
download a potentially vulnerable library version and replace a more
secure version already residing in the lib/ folder.

This patch adds two new options to the init program to disable the
automatic library download altogether, or selectively:

--skip-all-downloads switches the download mechanism off completely;

--skip-download <lib> switches the download off for the given library
  (<lib> being the section name of a library in libraries.config).

Change-Id: I1df60b2fd7a4bf519b135e16deebb68a3b9095ef
Signed-off-by: Michael Ochmann <michael.ochmann@sap.com>
This commit is contained in:
Michael Ochmann
2016-02-09 15:17:31 +01:00
committed by Saša Živkov
parent 59c765652e
commit d94f25aed3
7 changed files with 88 additions and 3 deletions

View File

@@ -12,6 +12,8 @@ init - Initialize a new Gerrit server installation
[--list-plugins]
[--install-plugin=<PLUGIN_NAME>]
[--dev]
[--skip-all-downloads]
[--skip-download=<LIBRARY_NAME>]
--
== DESCRIPTION
@@ -56,6 +58,14 @@ objects these SQL statements have to be executed manually.
Install in developer mode. Default configuration settings are
chosen to run the Gerrit server as a developer.
--skip-all-downloads::
Do not automatically download and install required libraries. The
administrator must provision the required libraries in the lib/ folder.
--skip-download::
Do not automatically download and install the library with the given name.
The administrator must provision the required library in the lib/ folder.
== CONTEXT
This command can only be run on a server which has direct
connectivity to the metadata database, and local access to the

View File

@@ -40,6 +40,7 @@ import org.kohsuke.args4j.Option;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/** Initialize a new Gerrit installation. */
@@ -67,6 +68,12 @@ public class Init extends BaseInit {
usage = "Setup site with default options suitable for developers")
private boolean dev;
@Option(name = "--skip-all-downloads", usage = "Don't download libraries")
private boolean skipAllDownloads;
@Option(name = "--skip-download", usage = "Don't download given library")
private List<String> skippedDownloads;
@Inject
Browser browser;
@@ -146,6 +153,18 @@ public class Init extends BaseInit {
return dev;
}
@Override
protected boolean skipAllDownloads() {
return skipAllDownloads;
}
@Override
protected List<String> getSkippedDownloads() {
return skippedDownloads != null
? skippedDownloads
: Collections.<String> emptyList();
}
@Override
protected String getSecureStoreLib() {
return secureStoreLib;

View File

@@ -26,6 +26,7 @@ import com.google.gerrit.common.IoUtil;
import com.google.gerrit.pgm.init.api.ConsoleUI;
import com.google.gerrit.pgm.init.api.InitFlags;
import com.google.gerrit.pgm.init.api.InstallPlugins;
import com.google.gerrit.pgm.init.api.LibraryDownload;
import com.google.gerrit.pgm.util.SiteProgram;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.GerritServerConfigModule;
@@ -147,6 +148,14 @@ public class BaseInit extends SiteProgram {
return null;
}
protected boolean skipAllDownloads() {
return false;
}
protected List<String> getSkippedDownloads() {
return Collections.emptyList();
}
/**
* Invoked before site init is called.
*
@@ -252,6 +261,10 @@ public class BaseInit extends SiteProgram {
bind(String.class).annotatedWith(SecureStoreClassName.class)
.toProvider(Providers.of(secureStoreClassName));
bind(SecureStore.class).toProvider(SecureStoreProvider.class).in(SINGLETON);
bind(new TypeLiteral<List<String>>() {}).annotatedWith(
LibraryDownload.class).toInstance(getSkippedDownloads());
bind(Boolean.class).annotatedWith(
LibraryDownload.class).toInstance(skipAllDownloads());
}
});

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.pgm.init;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.pgm.init.api.LibraryDownload;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -30,6 +31,7 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
/** Standard {@link LibraryDownloader} instances derived from configuration. */
@Singleton
@@ -38,6 +40,8 @@ class Libraries {
"com/google/gerrit/pgm/init/libraries.config";
private final Provider<LibraryDownloader> downloadProvider;
private final List<String> skippedDownloads;
private final boolean skipAllDownloads;
/* final */LibraryDownloader bouncyCastlePGP;
/* final */LibraryDownloader bouncyCastleProvider;
@@ -49,9 +53,12 @@ class Libraries {
/* final */LibraryDownloader oracleDriver;
@Inject
Libraries(final Provider<LibraryDownloader> downloadProvider) {
Libraries(final Provider<LibraryDownloader> downloadProvider,
@LibraryDownload List<String> skippedDownloads,
@LibraryDownload Boolean skipAllDownloads) {
this.downloadProvider = downloadProvider;
this.skippedDownloads = skippedDownloads;
this.skipAllDownloads = skipAllDownloads;
init();
}
@@ -98,6 +105,7 @@ class Libraries {
for (String d : cfg.getStringList("library", n, "needs")) {
dl.addNeeds((LibraryDownloader) getClass().getDeclaredField(d).get(this));
}
dl.setSkipDownload(skipAllDownloads || skippedDownloads.contains(n));
}
private static String getOptional(Config cfg, String name, String key) {

View File

@@ -58,6 +58,7 @@ class LibraryDownloader {
private Path dst;
private boolean download; // download or copy
private boolean exists;
private boolean skipDownload;
@Inject
LibraryDownloader(ConsoleUI ui, SitePaths site) {
@@ -87,6 +88,10 @@ class LibraryDownloader {
needs.add(lib);
}
void setSkipDownload(boolean skipDownload) {
this.skipDownload = skipDownload;
}
void downloadRequired() {
setRequired(true);
download();
@@ -105,6 +110,10 @@ class LibraryDownloader {
}
private void download() {
if (skipDownload) {
return;
}
if (jarUrl == null || !jarUrl.contains("/")) {
throw new IllegalStateException("Invalid JarUrl for " + name);
}

View File

@@ -0,0 +1,25 @@
// Copyright (C) 2016 The Android Open Source Project
//
// 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.google.gerrit.pgm.init.api;
import com.google.inject.BindingAnnotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
public @interface LibraryDownload {
}

View File

@@ -26,6 +26,7 @@ import com.google.inject.Provider;
import org.junit.Test;
import java.nio.file.Paths;
import java.util.Collections;
public class LibrariesTest {
@Test
@@ -40,7 +41,7 @@ public class LibrariesTest {
public LibraryDownloader get() {
return new LibraryDownloader(ui, site);
}
});
}, Collections.<String> emptyList(), false);
assertNotNull(lib.bouncyCastleProvider);
assertNotNull(lib.mysqlDriver);