Skip to content

Commit

Permalink
Plugins: plugin script to set proper plugin config dir attributes
Browse files Browse the repository at this point in the history
Depending on how elasticsearch is installed, we have two scenarios to take into account that relate to user, group and permissions assigned to the config directory:

1) deb/rpm package: /etc/elasticsearch is root:elasticsearch 750 and the plugin script is run from root user
2) tar/zip archive: es config dir is most likely elasticsearch:elasticsearch and the plugin script is most likely run from elasticsearch user

When the plugin script copies over the plugin config dir within the es config dir, it should take care of setting the proper user, group and permissions, which vary depending on how elasticsearch was installed in the first place. Should be root:elasticsearch 750 if installed from a package, or elasticsearch:elasticsearch if installed from an archive.

This commit makes sure that the plugin script looks at user, group and permissions of the config dir and copies them over to the plugin config subdirectory, whatever they are, so that they get properly setup depending on how elasticsearch was installed in the first place. We also make sure that execute permissions are left untouched for files.

Relates to #11016
Closes #14048
  • Loading branch information
javanna committed Oct 13, 2015
1 parent f33d1d7 commit 5ca7879
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 13 deletions.
62 changes: 57 additions & 5 deletions core/src/main/java/org/elasticsearch/plugins/PluginManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
Expand Down Expand Up @@ -253,7 +251,7 @@ private void extract(PluginHandle pluginHandle, Terminal terminal, Path pluginFi
copyBinDirectory(sourcePluginBinDirectory, destPluginBinDirectory, pluginHandle.name, terminal);
} catch (IOException e) {
// rollback and remove potentially before installed leftovers
terminal.printError("Error copying bin directory [%s] to [%s], cleaning up, reason: %s", sourcePluginBinDirectory, destPluginBinDirectory, e.getMessage());
terminal.printError("Error copying bin directory [%s] to [%s], cleaning up, reason: %s", sourcePluginBinDirectory, destPluginBinDirectory, ExceptionsHelper.detailedMessage(e));
tryToDeletePath(terminal, extractLocation, pluginHandle.binDir(environment));
throw e;
}
Expand All @@ -272,15 +270,69 @@ private void extract(PluginHandle pluginHandle, Terminal terminal, Path pluginFi
try {
terminal.println(VERBOSE, "Found config, moving to %s", destConfigDirectory.toAbsolutePath());
moveFilesWithoutOverwriting(sourceConfigDirectory, destConfigDirectory, ".new");

if (Environment.getFileStore(destConfigDirectory).supportsFileAttributeView(PosixFileAttributeView.class)) {
//We copy owner, group and permissions from the parent ES_CONFIG directory, assuming they were properly set depending
// on how es was installed in the first place: can be root:elasticsearch (750) if es was installed from rpm/deb packages
// or most likely elasticsearch:elasticsearch if installed from tar/zip. As for permissions we don't rely on umask.
final PosixFileAttributes parentDirAttributes = Files.getFileAttributeView(destConfigDirectory.getParent(), PosixFileAttributeView.class).readAttributes();
//for files though, we make sure not to copy execute permissions from the parent dir and leave them untouched
final Set<PosixFilePermission> baseFilePermissions = new HashSet<>();
for (PosixFilePermission posixFilePermission : parentDirAttributes.permissions()) {
switch (posixFilePermission) {
case OWNER_EXECUTE:
case GROUP_EXECUTE:
case OTHERS_EXECUTE:
break;
default:
baseFilePermissions.add(posixFilePermission);
}
}
Files.walkFileTree(destConfigDirectory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (attrs.isRegularFile()) {
Set<PosixFilePermission> newFilePermissions = new HashSet<>(baseFilePermissions);
Set<PosixFilePermission> currentFilePermissions = Files.getPosixFilePermissions(file);
for (PosixFilePermission posixFilePermission : currentFilePermissions) {
switch (posixFilePermission) {
case OWNER_EXECUTE:
case GROUP_EXECUTE:
case OTHERS_EXECUTE:
newFilePermissions.add(posixFilePermission);
}
}
setPosixFileAttributes(file, parentDirAttributes.owner(), parentDirAttributes.group(), newFilePermissions);
}
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
setPosixFileAttributes(dir, parentDirAttributes.owner(), parentDirAttributes.group(), parentDirAttributes.permissions());
return FileVisitResult.CONTINUE;
}
});
} else {
terminal.println(VERBOSE, "Skipping posix permissions - filestore doesn't support posix permission");
}

terminal.println(VERBOSE, "Installed %s into %s", pluginHandle.name, destConfigDirectory.toAbsolutePath());
} catch (IOException e) {
terminal.printError("Error copying config directory [%s] to [%s], cleaning up, reason: %s", sourceConfigDirectory, destConfigDirectory, e.getMessage());
terminal.printError("Error copying config directory [%s] to [%s], cleaning up, reason: %s", sourceConfigDirectory, destConfigDirectory, ExceptionsHelper.detailedMessage(e));
tryToDeletePath(terminal, extractLocation, destPluginBinDirectory, destConfigDirectory);
throw e;
}
}
}

private static void setPosixFileAttributes(Path path, UserPrincipal owner, GroupPrincipal group, Set<PosixFilePermission> permissions) throws IOException {
PosixFileAttributeView fileAttributeView = Files.getFileAttributeView(path, PosixFileAttributeView.class);
fileAttributeView.setOwner(owner);
fileAttributeView.setGroup(group);
fileAttributeView.setPermissions(permissions);
}

private void tryToDeletePath(Terminal terminal, Path ... paths) {
for (Path path : paths) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@
import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFilePermissions;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.*;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

Expand Down Expand Up @@ -250,6 +254,34 @@ public void testThatBinDirectoryBeingAFileAbortsInstallationAndDoesNotAccidental
}
}

public void testConfigDirectoryOwnerGroupAndPermissions() throws IOException {
assumeTrue("File system does not support permissions, skipping", supportsPermissions);
URL pluginUrl = createPlugin(false, true);
PluginManager pluginManager = new PluginManager(environment, pluginUrl, PluginManager.OutputMode.VERBOSE, TimeValue.timeValueSeconds(10));
pluginManager.downloadAndExtract(pluginName, terminal);
PosixFileAttributes parentFileAttributes = Files.getFileAttributeView(environment.configFile(), PosixFileAttributeView.class).readAttributes();
Path configPath = environment.configFile().resolve(pluginName);
PosixFileAttributes pluginConfigDirAttributes = Files.getFileAttributeView(configPath, PosixFileAttributeView.class).readAttributes();
assertThat(pluginConfigDirAttributes.owner(), equalTo(parentFileAttributes.owner()));
assertThat(pluginConfigDirAttributes.group(), equalTo(parentFileAttributes.group()));
assertThat(pluginConfigDirAttributes.permissions(), equalTo(parentFileAttributes.permissions()));
Path configFile = configPath.resolve("my-custom-config.yaml");
PosixFileAttributes pluginConfigFileAttributes = Files.getFileAttributeView(configFile, PosixFileAttributeView.class).readAttributes();
assertThat(pluginConfigFileAttributes.owner(), equalTo(parentFileAttributes.owner()));
assertThat(pluginConfigFileAttributes.group(), equalTo(parentFileAttributes.group()));
Set<PosixFilePermission> expectedFilePermissions = new HashSet<>();
for (PosixFilePermission parentPermission : parentFileAttributes.permissions()) {
switch(parentPermission) {
case OWNER_EXECUTE:
case GROUP_EXECUTE:
case OTHERS_EXECUTE:
break;
default:
expectedFilePermissions.add(parentPermission);
}
}
assertThat(pluginConfigFileAttributes.permissions(), equalTo(expectedFilePermissions));
}

private URL createPlugin(boolean withBinDir, boolean withConfigDir) throws IOException {
final Path structure = createTempDir().resolve("fake-plugin");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ assert_file() {
fi

if [ "x$user" != "x" ]; then
realuser=$(ls -ld "$file" | awk '{print $3}')
realuser=$(find "$file" -maxdepth 0 -printf "%u")
[ "$realuser" = "$user" ]
fi

Expand Down
21 changes: 17 additions & 4 deletions qa/vagrant/src/test/resources/packaging/scripts/plugins.bash
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,35 @@ remove_plugin() {
fi
}

# Install the jvm-example plugin which fully excercises the special case file
# Install the jvm-example plugin which fully exercises the special case file
# placements for non-site plugins.
install_jvm_example() {
local relativePath=${1:-$(readlink -m jvm-example-*.zip)}
install_jvm_plugin jvm-example "$relativePath"

assert_file_exist "$ESHOME/bin/jvm-example"
assert_file_exist "$ESHOME/bin/jvm-example/test"
assert_file_exist "$ESCONFIG/jvm-example"
assert_file_exist "$ESCONFIG/jvm-example/example.yaml"

#owner group and permissions vary depending on how es was installed
#just make sure that everything is the same as $CONFIG_DIR, which was properly set up during install
config_user=$(find "$ESCONFIG" -maxdepth 0 -printf "%u")
config_owner=$(find "$ESCONFIG" -maxdepth 0 -printf "%g")
config_privileges=$(find "$ESCONFIG" -maxdepth 0 -printf "%m")
assert_file "$ESCONFIG/jvm-example" d $config_user $config_owner $config_privileges
#the original file has no execute permissions and that must not change, but all other permissions
#need to be inherited from the parent config dir. We check this by applying the 111 mask to the config dir privileges.
for i in `seq 0 2`; do
current_perm_dir=${config_privileges:$i:1}
final_perm=$(($current_perm_dir & ~1))
expected_file_privileges+=$final_perm
done
assert_file "$ESCONFIG/jvm-example/example.yaml" f $config_user $config_owner $expected_file_privileges

echo "Running jvm-example's bin script...."
"$ESHOME/bin/jvm-example/test" | grep test
}

# Remove the jvm-example plugin which fully excercises the special cases of
# Remove the jvm-example plugin which fully exercises the special cases of
# removing bin and not removing config.
remove_jvm_example() {
remove_plugin jvm-example
Expand Down

0 comments on commit 5ca7879

Please sign in to comment.