Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#4292] Fix mounted data path directory permissions for besu user #7575

Merged
merged 12 commits into from
Sep 18, 2024
Merged
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@
- Remove privacy test classes support [#7569](https://github.com/hyperledger/besu/pull/7569)

### Bug fixes
- Layered txpool: do not send notifications when moving tx between layers [#7539](https://github.com/hyperledger/besu/pull/7539)
- Layered txpool: fix for unsent drop notifications on remove [#7538](https://github.com/hyperledger/besu/pull/7538)
- Honor block number or tag parameter in eth_estimateGas and eth_createAccessList [#7502](https://github.com/hyperledger/besu/pull/7502)
pullurib marked this conversation as resolved.
Show resolved Hide resolved
- Fix mounted data path directory permissions for besu user [#7575](https://github.com/hyperledger/besu/pull/7575)
- Fix for `debug_traceCall` to handle transactions without specified gas price. [#7510](https://github.com/hyperledger/besu/pull/7510)


## 24.9.1

### Upcoming Breaking Changes
Expand Down
93 changes: 78 additions & 15 deletions besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.hyperledger.besu.cli.DefaultCommandValues.getDefaultBesuDataPath;
Expand Down Expand Up @@ -203,17 +204,22 @@
import org.hyperledger.besu.util.number.Percentage;
import org.hyperledger.besu.util.number.PositiveNumber;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.URI;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.GroupPrincipal;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.UserPrincipal;
import java.time.Clock;
Expand All @@ -235,6 +241,7 @@
import java.util.stream.Collectors;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
Expand Down Expand Up @@ -385,10 +392,20 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
arity = "1")
private final Optional<String> identityString = Optional.empty();

private Boolean printPathsAndExit = Boolean.FALSE;
private String besuUserName = "besu";

@Option(
names = "--print-paths-and-exit",
description = "Print the configured paths and exit without starting the node.")
private final Boolean printPathsAndExit = false;
paramLabel = "<username>",
description = "Print the configured paths and exit without starting the node.",
arity = "0..1")
void setUserName(final String userName) {
if (userName != null) {
besuUserName = userName;
}
printPathsAndExit = Boolean.TRUE;
}

// P2P Discovery Option Group
@CommandLine.ArgGroup(validate = false, heading = "@|bold P2P Discovery Options|@%n")
Expand Down Expand Up @@ -1103,7 +1120,7 @@ public void run() {

if (printPathsAndExit) {
// Print configured paths requiring read/write permissions to be adjusted
checkPermissionsAndPrintPaths();
checkPermissionsAndPrintPaths(besuUserName);
System.exit(0); // Exit before any services are started
}

Expand Down Expand Up @@ -1152,17 +1169,19 @@ public void run() {
}
}

private void checkPermissionsAndPrintPaths() {
private void checkPermissionsAndPrintPaths(final String userName) {
// Check permissions for the data path
checkPermissions(dataDir(), "besu", false);
checkPermissions(dataDir(), userName, false);

// Check permissions for genesis file
try {
if (genesisFile != null) {
checkPermissions(genesisFile.toPath(), "besu", true);
checkPermissions(genesisFile.toPath(), userName, true);
}
} catch (Exception e) {
System.out.println("Error: Failed checking genesis file: Reason: " + e.getMessage());
commandLine
.getOut()
.println("Error: Failed checking genesis file: Reason: " + e.getMessage());
}
}

Expand All @@ -1171,7 +1190,8 @@ private void checkPermissions(final Path path, final String besuUser, final bool
try {
// Get the permissions of the file
// check if besu user is the owner - get owner permissions if yes
// else, check if besu and owner are in the same group - if yes, check the group permission
// else, check if besu user and owner are in the same group - if yes, check the group
// permission
// otherwise check permissions for others

// Get the owner of the file or directory
Expand All @@ -1187,21 +1207,64 @@ private void checkPermissions(final Path path, final String besuUser, final bool
hasReadPermission = permissions.contains(PosixFilePermission.OWNER_READ);
hasWritePermission = permissions.contains(PosixFilePermission.OWNER_WRITE);
} else {
// Skipping group check for now as we know besu user and root don't have a common group
// Others' permissions
hasReadPermission = permissions.contains(PosixFilePermission.OTHERS_READ);
hasWritePermission = permissions.contains(PosixFilePermission.OTHERS_WRITE);
// Get the group of the file
// Get POSIX file attributes and then group
PosixFileAttributes attrs = Files.readAttributes(path, PosixFileAttributes.class);
GroupPrincipal group = attrs.group();

// Check if besu user belongs to this group
boolean isMember = isGroupMember(besuUserName, group);

if (isMember) {
// Group's permissions
hasReadPermission = permissions.contains(PosixFilePermission.GROUP_READ);
hasWritePermission = permissions.contains(PosixFilePermission.GROUP_WRITE);
} else {
// Others' permissions
hasReadPermission = permissions.contains(PosixFilePermission.OTHERS_READ);
hasWritePermission = permissions.contains(PosixFilePermission.OTHERS_WRITE);
}
}

if (!hasReadPermission || (!readOnly && !hasWritePermission)) {
String accessType = readOnly ? "READ" : "READ_WRITE";
System.out.println("PERMISSION_CHECK_PATH:" + path + ":" + accessType);
commandLine.getOut().println("PERMISSION_CHECK_PATH:" + path + ":" + accessType);
}
} catch (Exception e) {
// Do nothing upon catching an error
System.out.println(
"Error: Failed to check permissions for path: '" + path + "'. Reason: " + e.getMessage());
commandLine
.getOut()
.println(
"Error: Failed to check permissions for path: '"
+ path
+ "'. Reason: "
+ e.getMessage());
}
}

private static boolean isGroupMember(final String userName, final GroupPrincipal group)
throws IOException {
// Get the groups of the user by executing 'id -Gn username'
Process process = Runtime.getRuntime().exec(new String[] {"id", "-Gn", userName});
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream(), UTF_8));

// Read the output of the command
String line = reader.readLine();
boolean isMember = false;
if (line != null) {
// Split the groups
Iterable<String> userGroups = Splitter.on(" ").split(line);
// Check if any of the user's groups match the file's group

for (String grp : userGroups) {
if (grp.equals(group.getName())) {
isMember = true;
break;
}
}
}
return isMember;
}

@VisibleForTesting
Expand Down
14 changes: 9 additions & 5 deletions besu/src/main/scripts/besu-entry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,28 @@
##

# Run Besu first to get paths needing permission adjustment
output=$(/opt/besu/bin/besu --print-paths-and-exit "$@")
output=$(/opt/besu/bin/besu --print-paths-and-exit $BESU_USER_NAME "$@")

# Parse the output to find the paths and their required access types
echo "$output" | while IFS=: read -r prefix path accessType; do
if [[ "$prefix" == "PERMISSION_CHECK_PATH" ]]; then
# Change ownership to besu user and group
chown -R besu:besu $path
chown -R $BESU_USER_NAME:$BESU_USER_NAME $path

# Ensure read/write permissions for besu user

echo "Setting permissions for: $path with access: $accessType"

if [[ "$accessType" == "READ" ]]; then
# Set read-only permissions for besu user
chmod -R u+r $path
# Add execute for directories to allow access
find $path -type d -exec chmod u+rx {} \;
find $path -type f -exec chmod u+r {} \;
elif [[ "$accessType" == "READ_WRITE" ]]; then
# Set read/write permissions for besu user
chmod -R u+rw $path
# Add execute for directories to allow access
find $path -type d -exec chmod u+rwx {} \;
find $path -type f -exec chmod u+rw {} \;
fi
fi
done
Expand All @@ -42,4 +46,4 @@ done
COMMAND="/opt/besu/bin/besu $@"

# Switch to the besu user and execute the command
exec su -s /bin/bash besu -c "$COMMAND"
exec su -s /bin/bash $BESU_USER_NAME -c "$COMMAND"
2 changes: 0 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,6 @@ task evmToolStartScripts(type: CreateStartScripts) {
doLast { tweakStartScript(evmToolStartScripts) }
}


task autocomplete(type: JavaExec) {
dependsOn compileJava
def tempAutocompleteFile = File.createTempFile("besu", ".autocomplete")
Expand Down Expand Up @@ -1098,7 +1097,6 @@ distributions {
from("build/reports/license/license-dependency.html") { into "." }
from("./docs/GettingStartedBinaries.md") { into "." }
from("./docs/DocsArchive0.8.0.html") { into "." }
from("./docs/DocsArchive0.8.0.html") { into "." }
from("./besu/src/main/scripts/besu-entry.sh") { into "./bin/" }
from(autocomplete) { into "." }
}
Expand Down
5 changes: 4 additions & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ RUN apt-get update $NO_PROXY_CACHE && \
chown besu:besu /opt/besu && \
chmod 0755 /opt/besu

USER besu
ARG BESU_USER=besu
USER ${BESU_USER}
WORKDIR /opt/besu

COPY --chown=besu:besu besu /opt/besu/
Expand Down Expand Up @@ -46,6 +47,8 @@ ENV PATH="/opt/besu/bin:${OLDPATH}"
USER root
RUN chmod +x /opt/besu/bin/besu-entry.sh

ENV BESU_USER_NAME=${BESU_USER}

ENTRYPOINT ["besu-entry.sh"]
HEALTHCHECK --start-period=5s --interval=5s --timeout=1s --retries=10 CMD bash -c "[ -f /tmp/pid ]"

Expand Down