Settings: Move keystore creation to plugin installation (#26329)
This commit removes the keystore creation on elasticsearch startup, and instead adds a plugin property which indicates the plugin needs the keystore to exist. It does still make sure the keystore.seed exists on ES startup, but through an "upgrade" method that loading the keystore in Bootstrap calls. closes #26309
This commit is contained in:
parent
7fb716daab
commit
5202e7e93b
|
@ -46,6 +46,10 @@ class PluginPropertiesExtension {
|
|||
@Input
|
||||
boolean hasClientJar = false
|
||||
|
||||
/** True if the plugin requires the elasticsearch keystore to exist, false otherwise. */
|
||||
@Input
|
||||
boolean requiresKeystore = false
|
||||
|
||||
/** A license file that should be included in the built plugin zip. */
|
||||
@Input
|
||||
File licenseFile = null
|
||||
|
|
|
@ -80,7 +80,8 @@ class PluginPropertiesTask extends Copy {
|
|||
'elasticsearchVersion': stringSnap(VersionProperties.elasticsearch),
|
||||
'javaVersion': project.targetCompatibility as String,
|
||||
'classname': extension.classname,
|
||||
'hasNativeController': extension.hasNativeController
|
||||
'hasNativeController': extension.hasNativeController,
|
||||
'requiresKeystore': extension.requiresKeystore
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,3 +42,6 @@ elasticsearch.version=${elasticsearchVersion}
|
|||
#
|
||||
# 'has.native.controller': whether or not the plugin has a native controller
|
||||
has.native.controller=${hasNativeController}
|
||||
#
|
||||
# 'requires.keystore': whether or not the plugin needs the elasticsearch keystore be created
|
||||
requires.keystore=${requiresKeystore}
|
||||
|
|
|
@ -226,16 +226,13 @@ final class Bootstrap {
|
|||
} catch (IOException e) {
|
||||
throw new BootstrapException(e);
|
||||
}
|
||||
if (keystore == null) {
|
||||
return null; // no keystore
|
||||
}
|
||||
|
||||
try {
|
||||
if (keystore == null) {
|
||||
// create it, we always want one! we use an empty passphrase, but a user can change this later if they want.
|
||||
KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.create(new char[0]);
|
||||
keyStoreWrapper.save(initialEnv.configFile());
|
||||
return keyStoreWrapper;
|
||||
} else {
|
||||
keystore.decrypt(new char[0] /* TODO: read password from stdin */);
|
||||
}
|
||||
keystore.decrypt(new char[0] /* TODO: read password from stdin */);
|
||||
KeyStoreWrapper.upgrade(keystore, initialEnv.configFile());
|
||||
} catch (Exception e) {
|
||||
throw new BootstrapException(e);
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ public class KeyStoreWrapper implements SecureSettings {
|
|||
}
|
||||
|
||||
/** Returns a path representing the ES keystore in the given config dir. */
|
||||
static Path keystorePath(Path configDir) {
|
||||
public static Path keystorePath(Path configDir) {
|
||||
return configDir.resolve(KEYSTORE_FILENAME);
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,8 @@ public class KeyStoreWrapper implements SecureSettings {
|
|||
}
|
||||
|
||||
/** Add the bootstrap seed setting, which may be used as a unique, secure, random value by the node */
|
||||
private static void addBootstrapSeed(KeyStoreWrapper wrapper) throws GeneralSecurityException {
|
||||
public static void addBootstrapSeed(KeyStoreWrapper wrapper) throws GeneralSecurityException {
|
||||
assert wrapper.getSettingNames().contains(SEED_SETTING.getKey()) == false;
|
||||
SecureRandom random = Randomness.createSecure();
|
||||
int passwordLength = 20; // Generate 20 character passwords
|
||||
char[] characters = new char[passwordLength];
|
||||
|
@ -227,6 +228,16 @@ public class KeyStoreWrapper implements SecureSettings {
|
|||
}
|
||||
}
|
||||
|
||||
/** Upgrades the format of the keystore, if necessary. */
|
||||
public static void upgrade(KeyStoreWrapper wrapper, Path configDir) throws Exception {
|
||||
// ensure keystore.seed exists
|
||||
if (wrapper.getSettingNames().contains(SEED_SETTING.getKey())) {
|
||||
return;
|
||||
}
|
||||
addBootstrapSeed(wrapper);
|
||||
wrapper.save(configDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoaded() {
|
||||
return keystore.get() != null;
|
||||
|
@ -252,11 +263,9 @@ public class KeyStoreWrapper implements SecureSettings {
|
|||
} finally {
|
||||
Arrays.fill(keystoreBytes, (byte)0);
|
||||
}
|
||||
|
||||
keystorePassword.set(new KeyStore.PasswordProtection(password));
|
||||
Arrays.fill(password, '\0');
|
||||
|
||||
|
||||
Enumeration<String> aliases = keystore.get().aliases();
|
||||
if (formatVersion == 1) {
|
||||
while (aliases.hasMoreElements()) {
|
||||
|
@ -279,6 +288,7 @@ public class KeyStoreWrapper implements SecureSettings {
|
|||
|
||||
/** Write the keystore to the given config directory. */
|
||||
public void save(Path configDir) throws Exception {
|
||||
assert isLoaded();
|
||||
char[] password = this.keystorePassword.get().getPassword();
|
||||
|
||||
SimpleFSDirectory directory = new SimpleFSDirectory(configDir);
|
||||
|
@ -332,6 +342,7 @@ public class KeyStoreWrapper implements SecureSettings {
|
|||
// TODO: make settings accessible only to code that registered the setting
|
||||
@Override
|
||||
public SecureString getString(String setting) throws GeneralSecurityException {
|
||||
assert isLoaded();
|
||||
KeyStore.Entry entry = keystore.get().getEntry(setting, keystorePassword.get());
|
||||
if (settingTypes.get(setting) != KeyType.STRING ||
|
||||
entry instanceof KeyStore.SecretKeyEntry == false) {
|
||||
|
@ -347,6 +358,7 @@ public class KeyStoreWrapper implements SecureSettings {
|
|||
|
||||
@Override
|
||||
public InputStream getFile(String setting) throws GeneralSecurityException {
|
||||
assert isLoaded();
|
||||
KeyStore.Entry entry = keystore.get().getEntry(setting, keystorePassword.get());
|
||||
if (settingTypes.get(setting) != KeyType.FILE ||
|
||||
entry instanceof KeyStore.SecretKeyEntry == false) {
|
||||
|
@ -377,6 +389,7 @@ public class KeyStoreWrapper implements SecureSettings {
|
|||
* @throws IllegalArgumentException if the value is not ASCII
|
||||
*/
|
||||
void setString(String setting, char[] value) throws GeneralSecurityException {
|
||||
assert isLoaded();
|
||||
if (ASCII_ENCODER.canEncode(CharBuffer.wrap(value)) == false) {
|
||||
throw new IllegalArgumentException("Value must be ascii");
|
||||
}
|
||||
|
@ -387,6 +400,7 @@ public class KeyStoreWrapper implements SecureSettings {
|
|||
|
||||
/** Set a file setting. */
|
||||
void setFile(String setting, byte[] bytes) throws GeneralSecurityException {
|
||||
assert isLoaded();
|
||||
bytes = Base64.getEncoder().encode(bytes);
|
||||
char[] chars = new char[bytes.length];
|
||||
for (int i = 0; i < chars.length; ++i) {
|
||||
|
@ -399,6 +413,7 @@ public class KeyStoreWrapper implements SecureSettings {
|
|||
|
||||
/** Remove the given setting from the keystore. */
|
||||
void remove(String setting) throws KeyStoreException {
|
||||
assert isLoaded();
|
||||
keystore.get().deleteEntry(setting);
|
||||
settingTypes.remove(setting);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.elasticsearch.plugins;
|
|||
public class DummyPluginInfo extends PluginInfo {
|
||||
|
||||
private DummyPluginInfo(String name, String description, String version, String classname) {
|
||||
super(name, description, version, classname, false);
|
||||
super(name, description, version, classname, false, false);
|
||||
}
|
||||
|
||||
public static final DummyPluginInfo INSTANCE =
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.plugins;
|
|||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.bootstrap.JarHell;
|
||||
import org.elasticsearch.common.Booleans;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
|
@ -48,6 +49,7 @@ public class PluginInfo implements Writeable, ToXContentObject {
|
|||
private final String version;
|
||||
private final String classname;
|
||||
private final boolean hasNativeController;
|
||||
private final boolean requiresKeystore;
|
||||
|
||||
/**
|
||||
* Construct plugin info.
|
||||
|
@ -57,18 +59,16 @@ public class PluginInfo implements Writeable, ToXContentObject {
|
|||
* @param version the version of Elasticsearch the plugin is built for
|
||||
* @param classname the entry point to the plugin
|
||||
* @param hasNativeController whether or not the plugin has a native controller
|
||||
* @param requiresKeystore whether or not the plugin requires the elasticsearch keystore to be created
|
||||
*/
|
||||
public PluginInfo(
|
||||
final String name,
|
||||
final String description,
|
||||
final String version,
|
||||
final String classname,
|
||||
final boolean hasNativeController) {
|
||||
public PluginInfo(String name, String description, String version, String classname,
|
||||
boolean hasNativeController, boolean requiresKeystore) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.version = version;
|
||||
this.classname = classname;
|
||||
this.hasNativeController = hasNativeController;
|
||||
this.requiresKeystore = requiresKeystore;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,6 +87,11 @@ public class PluginInfo implements Writeable, ToXContentObject {
|
|||
} else {
|
||||
hasNativeController = false;
|
||||
}
|
||||
if (in.getVersion().onOrAfter(Version.V_6_0_0_beta2)) {
|
||||
requiresKeystore = in.readBoolean();
|
||||
} else {
|
||||
requiresKeystore = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -98,6 +103,9 @@ public class PluginInfo implements Writeable, ToXContentObject {
|
|||
if (out.getVersion().onOrAfter(Version.V_5_4_0)) {
|
||||
out.writeBoolean(hasNativeController);
|
||||
}
|
||||
if (out.getVersion().onOrAfter(Version.V_6_0_0_beta2)) {
|
||||
out.writeBoolean(requiresKeystore);
|
||||
}
|
||||
}
|
||||
|
||||
/** reads (and validates) plugin metadata descriptor file */
|
||||
|
@ -173,17 +181,26 @@ public class PluginInfo implements Writeable, ToXContentObject {
|
|||
break;
|
||||
default:
|
||||
final String message = String.format(
|
||||
Locale.ROOT,
|
||||
"property [%s] must be [%s], [%s], or unspecified but was [%s]",
|
||||
"has_native_controller",
|
||||
"true",
|
||||
"false",
|
||||
hasNativeControllerValue);
|
||||
Locale.ROOT,
|
||||
"property [%s] must be [%s], [%s], or unspecified but was [%s]",
|
||||
"has_native_controller",
|
||||
"true",
|
||||
"false",
|
||||
hasNativeControllerValue);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
return new PluginInfo(name, description, version, classname, hasNativeController);
|
||||
final String requiresKeystoreValue = props.getProperty("requires.keystore", "false");
|
||||
final boolean requiresKeystore;
|
||||
try {
|
||||
requiresKeystore = Booleans.parseBoolean(requiresKeystoreValue);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException("property [requires.keystore] must be [true] or [false]," +
|
||||
" but was [" + requiresKeystoreValue + "]", e);
|
||||
}
|
||||
|
||||
return new PluginInfo(name, description, version, classname, hasNativeController, requiresKeystore);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -231,6 +248,15 @@ public class PluginInfo implements Writeable, ToXContentObject {
|
|||
return hasNativeController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the plugin requires the elasticsearch keystore to exist.
|
||||
*
|
||||
* @return {@code true} if the plugin requires a keystore, {@code false} otherwise
|
||||
*/
|
||||
public boolean requiresKeystore() {
|
||||
return requiresKeystore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
|
@ -240,6 +266,7 @@ public class PluginInfo implements Writeable, ToXContentObject {
|
|||
builder.field("description", description);
|
||||
builder.field("classname", classname);
|
||||
builder.field("has_native_controller", hasNativeController);
|
||||
builder.field("requires_keystore", requiresKeystore);
|
||||
}
|
||||
builder.endObject();
|
||||
|
||||
|
@ -272,6 +299,7 @@ public class PluginInfo implements Writeable, ToXContentObject {
|
|||
.append("Description: ").append(description).append("\n")
|
||||
.append("Version: ").append(version).append("\n")
|
||||
.append("Native Controller: ").append(hasNativeController).append("\n")
|
||||
.append("Requires Keystore: ").append(requiresKeystore).append("\n")
|
||||
.append(" * Classname: ").append(classname);
|
||||
return information.toString();
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ public class PluginsService extends AbstractComponent {
|
|||
// first we load plugins that are on the classpath. this is for tests and transport clients
|
||||
for (Class<? extends Plugin> pluginClass : classpathPlugins) {
|
||||
Plugin plugin = loadPlugin(pluginClass, settings, configPath);
|
||||
PluginInfo pluginInfo = new PluginInfo(pluginClass.getName(), "classpath plugin", "NA", pluginClass.getName(), false);
|
||||
PluginInfo pluginInfo = new PluginInfo(pluginClass.getName(), "classpath plugin", "NA", pluginClass.getName(), false, false);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("plugin loaded from classpath [{}]", pluginInfo);
|
||||
}
|
||||
|
|
|
@ -50,13 +50,6 @@ public class BootstrapTests extends ESTestCase {
|
|||
env = KeyStoreCommandTestCase.setupEnv(true, fileSystems);
|
||||
}
|
||||
|
||||
public void testLoadSecureSettingsCreatesKeystore() throws BootstrapException {
|
||||
final Path configPath = env.configFile();
|
||||
assertFalse(Files.exists(configPath.resolve("elasticsearch.keystore")));
|
||||
Bootstrap.loadSecureSettings(env);
|
||||
assertTrue(Files.exists(configPath.resolve("elasticsearch.keystore")));
|
||||
}
|
||||
|
||||
public void testLoadSecureSettings() throws Exception {
|
||||
final Path configPath = env.configFile();
|
||||
final SecureString seed;
|
||||
|
|
|
@ -69,8 +69,32 @@ public class KeyStoreWrapperTests extends ESTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testKeystoreSeed() throws Exception {
|
||||
public void testCreate() throws Exception {
|
||||
KeyStoreWrapper keystore = KeyStoreWrapper.create(new char[0]);
|
||||
assertTrue(keystore.getSettingNames().contains(KeyStoreWrapper.SEED_SETTING.getKey()));
|
||||
}
|
||||
|
||||
public void testUpgradeNoop() throws Exception {
|
||||
KeyStoreWrapper keystore = KeyStoreWrapper.create(new char[0]);
|
||||
SecureString seed = keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey());
|
||||
keystore.save(env.configFile());
|
||||
// upgrade does not overwrite seed
|
||||
KeyStoreWrapper.upgrade(keystore, env.configFile());
|
||||
assertEquals(seed.toString(), keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey()).toString());
|
||||
keystore = KeyStoreWrapper.load(env.configFile());
|
||||
keystore.decrypt(new char[0]);
|
||||
assertEquals(seed.toString(), keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey()).toString());
|
||||
}
|
||||
|
||||
public void testUpgradeAddsSeed() throws Exception {
|
||||
KeyStoreWrapper keystore = KeyStoreWrapper.create(new char[0]);
|
||||
keystore.remove(KeyStoreWrapper.SEED_SETTING.getKey());
|
||||
keystore.save(env.configFile());
|
||||
KeyStoreWrapper.upgrade(keystore, env.configFile());
|
||||
SecureString seed = keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey());
|
||||
assertNotNull(seed);
|
||||
keystore = KeyStoreWrapper.load(env.configFile());
|
||||
keystore.decrypt(new char[0]);
|
||||
assertEquals(seed.toString(), keystore.getString(KeyStoreWrapper.SEED_SETTING.getKey()).toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,13 +143,13 @@ public class NodeInfoStreamingTests extends ESTestCase {
|
|||
List<PluginInfo> plugins = new ArrayList<>();
|
||||
for (int i = 0; i < numPlugins; i++) {
|
||||
plugins.add(new PluginInfo(randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10),
|
||||
randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10), randomBoolean()));
|
||||
randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10), randomBoolean(), randomBoolean()));
|
||||
}
|
||||
int numModules = randomIntBetween(0, 5);
|
||||
List<PluginInfo> modules = new ArrayList<>();
|
||||
for (int i = 0; i < numModules; i++) {
|
||||
modules.add(new PluginInfo(randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10),
|
||||
randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10), randomBoolean()));
|
||||
randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10), randomBoolean(), randomBoolean()));
|
||||
}
|
||||
pluginsAndModules = new PluginsAndModules(plugins, modules);
|
||||
}
|
||||
|
|
|
@ -209,11 +209,11 @@ public class PluginInfoTests extends ESTestCase {
|
|||
|
||||
public void testPluginListSorted() {
|
||||
List<PluginInfo> plugins = new ArrayList<>();
|
||||
plugins.add(new PluginInfo("c", "foo", "dummy", "dummyclass", randomBoolean()));
|
||||
plugins.add(new PluginInfo("b", "foo", "dummy", "dummyclass", randomBoolean()));
|
||||
plugins.add(new PluginInfo("e", "foo", "dummy", "dummyclass", randomBoolean()));
|
||||
plugins.add(new PluginInfo("a", "foo", "dummy", "dummyclass", randomBoolean()));
|
||||
plugins.add(new PluginInfo("d", "foo", "dummy", "dummyclass", randomBoolean()));
|
||||
plugins.add(new PluginInfo("c", "foo", "dummy", "dummyclass", randomBoolean(), randomBoolean()));
|
||||
plugins.add(new PluginInfo("b", "foo", "dummy", "dummyclass", randomBoolean(), randomBoolean()));
|
||||
plugins.add(new PluginInfo("e", "foo", "dummy", "dummyclass", randomBoolean(), randomBoolean()));
|
||||
plugins.add(new PluginInfo("a", "foo", "dummy", "dummyclass", randomBoolean(), randomBoolean()));
|
||||
plugins.add(new PluginInfo("d", "foo", "dummy", "dummyclass", randomBoolean(), randomBoolean()));
|
||||
PluginsAndModules pluginsInfo = new PluginsAndModules(plugins, Collections.emptyList());
|
||||
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.elasticsearch.common.SuppressForbidden;
|
|||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.hash.MessageDigests;
|
||||
import org.elasticsearch.common.io.FileSystemUtils;
|
||||
import org.elasticsearch.common.settings.KeyStoreWrapper;
|
||||
import org.elasticsearch.env.Environment;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
@ -572,6 +573,15 @@ class InstallPluginCommand extends EnvironmentAwareCommand {
|
|||
}
|
||||
});
|
||||
|
||||
if (info.requiresKeystore()) {
|
||||
KeyStoreWrapper keystore = KeyStoreWrapper.load(env.configFile());
|
||||
if (keystore == null) {
|
||||
terminal.println("Elasticsearch keystore is required by plugin [" + info.getName() + "], creating...");
|
||||
keystore = KeyStoreWrapper.create(new char[0]);
|
||||
keystore.save(env.configFile());
|
||||
}
|
||||
}
|
||||
|
||||
terminal.println("-> Installed " + info.getName());
|
||||
|
||||
} catch (Exception installProblem) {
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.elasticsearch.common.collect.Tuple;
|
|||
import org.elasticsearch.common.io.FileSystemUtils;
|
||||
import org.elasticsearch.common.io.PathUtils;
|
||||
import org.elasticsearch.common.io.PathUtilsForTesting;
|
||||
import org.elasticsearch.common.settings.KeyStoreWrapper;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
@ -61,13 +62,16 @@ import java.nio.file.attribute.PosixFileAttributeView;
|
|||
import java.nio.file.attribute.PosixFileAttributes;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.nio.file.attribute.UserPrincipal;
|
||||
import java.security.KeyStore;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
|
@ -201,18 +205,20 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|||
}
|
||||
|
||||
/** creates a plugin .zip and returns the url for testing */
|
||||
static String createPluginUrl(String name, Path structure) throws IOException {
|
||||
return createPlugin(name, structure, false).toUri().toURL().toString();
|
||||
static String createPluginUrl(String name, Path structure, String... additionalProps) throws IOException {
|
||||
return createPlugin(name, structure, false, additionalProps).toUri().toURL().toString();
|
||||
}
|
||||
|
||||
static Path createPlugin(String name, Path structure, boolean createSecurityPolicyFile) throws IOException {
|
||||
PluginTestUtil.writeProperties(structure,
|
||||
static Path createPlugin(String name, Path structure, boolean createSecurityPolicyFile, String... additionalProps) throws IOException {
|
||||
String[] properties = Stream.concat(Stream.of(
|
||||
"description", "fake desc",
|
||||
"name", name,
|
||||
"version", "1.0",
|
||||
"elasticsearch.version", Version.CURRENT.toString(),
|
||||
"java.version", System.getProperty("java.specification.version"),
|
||||
"classname", "FakePlugin");
|
||||
"classname", "FakePlugin"
|
||||
), Arrays.stream(additionalProps)).toArray(String[]::new);
|
||||
PluginTestUtil.writeProperties(structure, properties);
|
||||
if (createSecurityPolicyFile) {
|
||||
String securityPolicyContent = "grant {\n permission java.lang.RuntimePermission \"setFactory\";\n};\n";
|
||||
Files.write(structure.resolve("plugin-security.policy"), securityPolicyContent.getBytes(StandardCharsets.UTF_8));
|
||||
|
@ -808,4 +814,32 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|||
}
|
||||
|
||||
// TODO: test checksum (need maven/official below)
|
||||
|
||||
public void testKeystoreNotRequired() throws Exception {
|
||||
Tuple<Path, Environment> env = createEnv(fs, temp);
|
||||
Path pluginDir = createPluginDir(temp);
|
||||
String pluginZip = createPluginUrl("fake", pluginDir, "requires.keystore", "false");
|
||||
installPlugin(pluginZip, env.v1());
|
||||
assertFalse(Files.exists(KeyStoreWrapper.keystorePath(env.v2().configFile())));
|
||||
}
|
||||
|
||||
public void testKeystoreRequiredAlreadyExists() throws Exception {
|
||||
Tuple<Path, Environment> env = createEnv(fs, temp);
|
||||
KeyStoreWrapper keystore = KeyStoreWrapper.create(new char[0]);
|
||||
keystore.save(env.v2().configFile());
|
||||
byte[] expectedBytes = Files.readAllBytes(KeyStoreWrapper.keystorePath(env.v2().configFile()));
|
||||
Path pluginDir = createPluginDir(temp);
|
||||
String pluginZip = createPluginUrl("fake", pluginDir, "requires.keystore", "true");
|
||||
installPlugin(pluginZip, env.v1());
|
||||
byte[] gotBytes = Files.readAllBytes(KeyStoreWrapper.keystorePath(env.v2().configFile()));
|
||||
assertArrayEquals("Keystore was modified", expectedBytes, gotBytes);
|
||||
}
|
||||
|
||||
public void testKeystoreRequiredCreated() throws Exception {
|
||||
Tuple<Path, Environment> env = createEnv(fs, temp);
|
||||
Path pluginDir = createPluginDir(temp);
|
||||
String pluginZip = createPluginUrl("fake", pluginDir, "requires.keystore", "true");
|
||||
MockTerminal terminal = installPlugin(pluginZip, env.v1());
|
||||
assertTrue(Files.exists(KeyStoreWrapper.keystorePath(env.v2().configFile())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ public class ListPluginsCommandTests extends ESTestCase {
|
|||
final String description,
|
||||
final String name,
|
||||
final String classname) throws IOException {
|
||||
buildFakePlugin(env, description, name, classname, false);
|
||||
buildFakePlugin(env, description, name, classname, false, false);
|
||||
}
|
||||
|
||||
private static void buildFakePlugin(
|
||||
|
@ -99,7 +99,8 @@ public class ListPluginsCommandTests extends ESTestCase {
|
|||
final String description,
|
||||
final String name,
|
||||
final String classname,
|
||||
final boolean hasNativeController) throws IOException {
|
||||
final boolean hasNativeController,
|
||||
final boolean requiresKeystore) throws IOException {
|
||||
PluginTestUtil.writeProperties(
|
||||
env.pluginsFile().resolve(name),
|
||||
"description", description,
|
||||
|
@ -108,7 +109,8 @@ public class ListPluginsCommandTests extends ESTestCase {
|
|||
"elasticsearch.version", Version.CURRENT.toString(),
|
||||
"java.version", System.getProperty("java.specification.version"),
|
||||
"classname", classname,
|
||||
"has.native.controller", Boolean.toString(hasNativeController));
|
||||
"has.native.controller", Boolean.toString(hasNativeController),
|
||||
"requires.keystore", Boolean.toString(requiresKeystore));
|
||||
}
|
||||
|
||||
public void testPluginsDirMissing() throws Exception {
|
||||
|
@ -148,25 +150,45 @@ public class ListPluginsCommandTests extends ESTestCase {
|
|||
"Description: fake desc",
|
||||
"Version: 1.0",
|
||||
"Native Controller: false",
|
||||
"Requires Keystore: false",
|
||||
" * Classname: org.fake"),
|
||||
terminal.getOutput());
|
||||
}
|
||||
|
||||
public void testPluginWithNativeController() throws Exception {
|
||||
buildFakePlugin(env, "fake desc 1", "fake_plugin1", "org.fake", true);
|
||||
buildFakePlugin(env, "fake desc 1", "fake_plugin1", "org.fake", true, false);
|
||||
String[] params = { "-v" };
|
||||
MockTerminal terminal = listPlugins(home, params);
|
||||
assertEquals(
|
||||
buildMultiline(
|
||||
"Plugins directory: " + env.pluginsFile(),
|
||||
"fake_plugin1",
|
||||
"- Plugin information:",
|
||||
"Name: fake_plugin1",
|
||||
"Description: fake desc 1",
|
||||
"Version: 1.0",
|
||||
"Native Controller: true",
|
||||
" * Classname: org.fake"),
|
||||
terminal.getOutput());
|
||||
buildMultiline(
|
||||
"Plugins directory: " + env.pluginsFile(),
|
||||
"fake_plugin1",
|
||||
"- Plugin information:",
|
||||
"Name: fake_plugin1",
|
||||
"Description: fake desc 1",
|
||||
"Version: 1.0",
|
||||
"Native Controller: true",
|
||||
"Requires Keystore: false",
|
||||
" * Classname: org.fake"),
|
||||
terminal.getOutput());
|
||||
}
|
||||
|
||||
public void testPluginWithRequiresKeystore() throws Exception {
|
||||
buildFakePlugin(env, "fake desc 1", "fake_plugin1", "org.fake", false, true);
|
||||
String[] params = { "-v" };
|
||||
MockTerminal terminal = listPlugins(home, params);
|
||||
assertEquals(
|
||||
buildMultiline(
|
||||
"Plugins directory: " + env.pluginsFile(),
|
||||
"fake_plugin1",
|
||||
"- Plugin information:",
|
||||
"Name: fake_plugin1",
|
||||
"Description: fake desc 1",
|
||||
"Version: 1.0",
|
||||
"Native Controller: false",
|
||||
"Requires Keystore: true",
|
||||
" * Classname: org.fake"),
|
||||
terminal.getOutput());
|
||||
}
|
||||
|
||||
public void testPluginWithVerboseMultiplePlugins() throws Exception {
|
||||
|
@ -183,6 +205,7 @@ public class ListPluginsCommandTests extends ESTestCase {
|
|||
"Description: fake desc 1",
|
||||
"Version: 1.0",
|
||||
"Native Controller: false",
|
||||
"Requires Keystore: false",
|
||||
" * Classname: org.fake",
|
||||
"fake_plugin2",
|
||||
"- Plugin information:",
|
||||
|
@ -190,6 +213,7 @@ public class ListPluginsCommandTests extends ESTestCase {
|
|||
"Description: fake desc 2",
|
||||
"Version: 1.0",
|
||||
"Native Controller: false",
|
||||
"Requires Keystore: false",
|
||||
" * Classname: org.fake2"),
|
||||
terminal.getOutput());
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public class PluginSecurityTests extends ESTestCase {
|
|||
"test cannot run with security manager enabled",
|
||||
System.getSecurityManager() == null);
|
||||
final PluginInfo info =
|
||||
new PluginInfo("fake", "fake", Version.CURRENT.toString(), "Fake", true);
|
||||
new PluginInfo("fake", "fake", Version.CURRENT.toString(), "Fake", true, false);
|
||||
final MockTerminal terminal = new MockTerminal();
|
||||
terminal.addTextInput("y");
|
||||
terminal.addTextInput("y");
|
||||
|
@ -63,7 +63,7 @@ public class PluginSecurityTests extends ESTestCase {
|
|||
"test cannot run with security manager enabled",
|
||||
System.getSecurityManager() == null);
|
||||
final PluginInfo info =
|
||||
new PluginInfo("fake", "fake", Version.CURRENT.toString(), "Fake", true);
|
||||
new PluginInfo("fake", "fake", Version.CURRENT.toString(), "Fake", true, false);
|
||||
final MockTerminal terminal = new MockTerminal();
|
||||
terminal.addTextInput("y");
|
||||
terminal.addTextInput("n");
|
||||
|
@ -79,7 +79,7 @@ public class PluginSecurityTests extends ESTestCase {
|
|||
"test cannot run with security manager enabled",
|
||||
System.getSecurityManager() == null);
|
||||
final PluginInfo info =
|
||||
new PluginInfo("fake", "fake", Version.CURRENT.toString(), "Fake", false);
|
||||
new PluginInfo("fake", "fake", Version.CURRENT.toString(), "Fake", false, false);
|
||||
final MockTerminal terminal = new MockTerminal();
|
||||
terminal.addTextInput("y");
|
||||
final Path policyFile = this.getDataPath("security/simple-plugin-security.policy");
|
||||
|
|
Loading…
Reference in New Issue