Skip to content

Commit

Permalink
Allow running under root on Linux when unshare is available
Browse files Browse the repository at this point in the history
  • Loading branch information
jameshilliard committed Aug 4, 2020
1 parent 5917444 commit 0fc492c
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
java: [8, 11, 13]
java: [8, 11, 13, 14]
steps:
- name: Checkout project
uses: actions/checkout@v1
Expand Down
32 changes: 16 additions & 16 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,12 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
<version>3.10</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.19</version>
<version>1.20</version>
</dependency>
<dependency>
<groupId>org.tukaani</groupId>
Expand All @@ -124,29 +124,29 @@
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
<version>2.7</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
<version>1.14</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>6.0.8</version>
<version>6.5.1</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>3.6.3</version>
<version>4.0.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.5</version>
<version>42.2.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
Expand All @@ -158,21 +158,21 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.2</version>
<version>5.6.2</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
<version>1.7.30</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.13.0</version>
<version>3.4.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand All @@ -181,7 +181,7 @@
<plugins>
<plugin>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.8</version>
<version>3.13.0</version>
<executions>
<execution>
<phase>verify</phase>
Expand All @@ -194,13 +194,13 @@
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-core</artifactId>
<version>5.6.1</version>
<version>6.25.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-java</artifactId>
<version>5.6.1</version>
<version>6.25.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
Expand All @@ -216,7 +216,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
Expand All @@ -229,7 +229,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.4</version>
<version>3.2.0</version>
<executions>
<execution>
<id>attach-javadocs</id>
Expand Down Expand Up @@ -263,7 +263,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@
package io.zonky.test.db.postgres.embedded;


import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
Expand Down Expand Up @@ -55,7 +50,6 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import javax.sql.DataSource;

Expand All @@ -72,6 +66,7 @@
import org.slf4j.LoggerFactory;
import org.tukaani.xz.XZInputStream;

import static io.zonky.test.db.postgres.util.LinuxUtils.isUnshareUseable;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.WRITE;
import static java.util.Collections.unmodifiableMap;
Expand Down Expand Up @@ -104,6 +99,7 @@ public class EmbeddedPostgres implements Closeable
private volatile FileOutputStream lockStream;
private volatile FileLock lock;
private final boolean cleanDataDirectory;
private static boolean useUnshare;

private final ProcessBuilder.Redirect errorRedirector;
private final ProcessBuilder.Redirect outputRedirector;
Expand Down Expand Up @@ -133,6 +129,8 @@ public class EmbeddedPostgres implements Closeable
this.pgStartupWait = pgStartupWait;
Objects.requireNonNull(this.pgStartupWait, "Wait time cannot be null");

useUnshare = isUnshareUseable();

if (parentDirectory != null) {
mkdirs(parentDirectory);
cleanOldDataDirectories(parentDirectory);
Expand Down Expand Up @@ -243,10 +241,10 @@ private void initdb()
watch.start();
List<String> command = new ArrayList<>();
command.addAll(Arrays.asList(
pgBin("initdb"), "-A", "trust", "-U", PG_SUPERUSER,
"-A", "trust", "-U", PG_SUPERUSER,
"-D", dataDirectory.getPath(), "-E", "UTF-8"));
command.addAll(createLocaleOptions());
system(command.toArray(new String[command.size()]));
system(pgBin("initdb"), command);
LOG.info("{} initdb completed in {}", instanceId, watch);
}

Expand All @@ -259,13 +257,11 @@ private void startPostmaster() throws IOException
}

final List<String> args = new ArrayList<>();
args.addAll(pgBin("postgres"));
args.addAll(Arrays.asList(
pgBin("pg_ctl"),
"-D", dataDirectory.getPath(),
"-o", createInitOptions().stream().collect(Collectors.joining(" ")),
"-w",
"start"
"-D", dataDirectory.getPath()
));
args.addAll(createInitOptions());

final ProcessBuilder builder = new ProcessBuilder(args);

Expand All @@ -275,7 +271,7 @@ private void startPostmaster() throws IOException
final Process postmaster = builder.start();

if (outputRedirector.type() == ProcessBuilder.Redirect.Type.PIPE) {
ProcessOutputLogger.logOutput(LOG, postmaster, "pg_ctl");
ProcessOutputLogger.logOutput(LOG, postmaster, "postgres");
}

LOG.info("{} postmaster started as {} on port {}. Waiting up to {} for server startup to finish.", instanceId, postmaster.toString(), port, pgStartupWait);
Expand Down Expand Up @@ -414,7 +410,13 @@ public void close() throws IOException

private void pgCtl(File dir, String action)
{
system(pgBin("pg_ctl"), "-D", dir.getPath(), action, "-m", PG_STOP_MODE, "-t", PG_STOP_WAIT_S, "-w");
final List<String> args = new ArrayList<>();
args.addAll(Arrays.asList(
"-D", dir.getPath(), action,
"-m", PG_STOP_MODE, "-t",
PG_STOP_WAIT_S, "-w"
));
system(pgBin("pg_ctl"), args);
}

private void cleanOldDataDirectories(File parentDirectory)
Expand Down Expand Up @@ -461,10 +463,17 @@ private void cleanOldDataDirectories(File parentDirectory)
}
}

private String pgBin(String binaryName)
private List<String> pgBin(String binaryName)
{
final List<String> args = new ArrayList<>();
if (useUnshare) {
args.addAll(Arrays.asList(
"unshare", "-U"
));
}
final String extension = SystemUtils.IS_OS_WINDOWS ? ".exe" : "";
return new File(pgDir, "bin/" + binaryName + extension).getPath();
args.add(new File(pgDir, "bin/" + binaryName + extension).getPath());
return args;
}

private static File getWorkingDirectory()
Expand Down Expand Up @@ -614,8 +623,11 @@ public int hashCode() {
}
}

private void system(String... command)
private void system(List<String> bin, List<String> args)
{
final List<String> command = new ArrayList<>();
command.addAll(bin);
command.addAll(args);
try {
final ProcessBuilder builder = new ProcessBuilder(command);
builder.redirectErrorStream(true);
Expand All @@ -624,7 +636,7 @@ private void system(String... command)
final Process process = builder.start();

if (outputRedirector.type() == ProcessBuilder.Redirect.Type.PIPE) {
String processName = command[0].replaceAll("^.*[\\\\/](\\w+)(\\.exe)?$", "$1");
String processName = bin.get(bin.size() - 1).replaceAll("^.*[\\\\/](\\w+)(\\.exe)?$", "$1");
ProcessOutputLogger.logOutput(LOG, process, processName);
}
if (0 != process.waitFor()) {
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/io/zonky/test/db/postgres/util/LinuxUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,13 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
Expand All @@ -34,12 +39,16 @@ public final class LinuxUtils {

private static final String DISTRIBUTION_NAME = resolveDistributionName();

private static final boolean UNSHARE_USEABLE = unshareUseable();

private LinuxUtils() {}

public static String getDistributionName() {
return DISTRIBUTION_NAME;
}

public static boolean isUnshareUseable() { return UNSHARE_USEABLE; }

private static String resolveDistributionName() {
if (!SystemUtils.IS_OS_LINUX) {
return null;
Expand Down Expand Up @@ -85,4 +94,47 @@ private static String resolveDistributionName() {
return null;
}
}

private static boolean unshareUseable() {
if (SystemUtils.IS_OS_LINUX) {
int uid;
try {
Class<?> c = Class.forName("com.sun.security.auth.module.UnixSystem");
Object o = c.getDeclaredConstructor().newInstance();
Method method = c.getDeclaredMethod("getUid");
uid = ((Number) method.invoke(o)).intValue();
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException |
NoSuchMethodException | InvocationTargetException e) {
return false;
}
if (uid == 0) {
final List<String> command = new ArrayList<>();
command.addAll(Arrays.asList(
"unshare", "-U",
"id", "-u"
));
final ProcessBuilder builder = new ProcessBuilder(command);
final Process process;
try {
process = builder.start();
} catch (IOException e) {
return false;
}
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
try {
process.waitFor();
} catch (InterruptedException e) {
return false;
}
try {
if (process.exitValue() == 0 && br.readLine() != "0") {
return true;
}
} catch (IOException e) {
return false;
}
}
}
return false;
}
}

0 comments on commit 0fc492c

Please sign in to comment.