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

add container arguments to specify SELinux contexts for mounts #334

Merged
merged 3 commits into from
Jun 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions core/src/main/java/org/testcontainers/containers/Container.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,24 @@ public String getStderr() {
* Adds a file system binding. Consider using {@link #withFileSystemBind(String, String, BindMode)}
* for building a container in a fluent style.
*
* @param hostPath the file system path on the host
* @param hostPath the file system path on the host
* @param containerPath the file system path inside the container
* @param mode the bind mode
* @param mode the bind mode
*/
default void addFileSystemBind(final String hostPath, final String containerPath, final BindMode mode) {
addFileSystemBind(hostPath, containerPath, mode, SelinuxContext.NONE);
}

/**
* Adds a file system binding. Consider using {@link #withFileSystemBind(String, String, BindMode)}
* for building a container in a fluent style.
*
* @param hostPath the file system path on the host
* @param containerPath the file system path inside the container
* @param mode the bind mode
* @param selinuxContext selinux context argument to use for this file
*/
void addFileSystemBind(String hostPath, String containerPath, BindMode mode);
void addFileSystemBind(String hostPath, String containerPath, BindMode mode, SelinuxContext selinuxContext);

/**
* Add a link to another container.
Expand Down Expand Up @@ -205,7 +218,22 @@ public String getStderr() {
* @param mode access mode for the file
* @return this
*/
SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode);
default SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode) {
withClasspathResourceMapping(resourcePath, containerPath, mode, SelinuxContext.NONE);
return self();
}

/**
* Map a resource (file or directory) on the classpath to a path inside the container.
* This will only work if you are running your tests outside a Docker container.
*
* @param resourcePath path to the resource on the classpath (relative to the classpath root; should not start with a leading slash)
* @param containerPath path this should be mapped to inside the container
* @param mode access mode for the file
* @param selinuxContext selinux context argument to use for this file
* @return this
*/
SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode, SelinuxContext selinuxContext);

/**
* Set the duration of waiting time until container treated as started.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -481,10 +481,10 @@ public void addEnv(String key, String value) {
* {@inheritDoc}
*/
@Override
public void addFileSystemBind(String hostPath, String containerPath, BindMode mode) {
public void addFileSystemBind(final String hostPath, final String containerPath, final BindMode mode, final SelinuxContext selinuxContext) {

final MountableFile mountableFile = MountableFile.forHostPath(hostPath);
binds.add(new Bind(mountableFile.getResolvedPath(), new Volume(containerPath), mode.accessMode));
binds.add(new Bind(mountableFile.getResolvedPath(), new Volume(containerPath), mode.accessMode, selinuxContext.selContext));
}

/**
Expand Down Expand Up @@ -615,10 +615,18 @@ public SELF withNetworkMode(String networkMode) {
* {@inheritDoc}
*/
@Override
public SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode) {
public SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode) {
return withClasspathResourceMapping(resourcePath, containerPath, mode, SelinuxContext.NONE);
}

/**
* {@inheritDoc}
*/
@Override
public SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode, final SelinuxContext selinuxContext) {
final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath);

this.addFileSystemBind(mountableFile.getResolvedPath(), containerPath, mode);
this.addFileSystemBind(mountableFile.getResolvedPath(), containerPath, mode, selinuxContext);

return self();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.testcontainers.containers;

import com.github.dockerjava.api.model.SELContext;
import lombok.AllArgsConstructor;

/**
* Possible contexts for use with SELinux
*/
@AllArgsConstructor
public enum SelinuxContext {
SHARED(SELContext.shared),
SINGLE(SELContext.single),
NONE(SELContext.none);

public final SELContext selContext;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about private modifier and @Getter on it? We usually use getters instead of fields access :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ping

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cainj13 ping :)


}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

import static org.rnorth.visibleassertions.VisibleAssertions.*;
import static org.testcontainers.containers.BindMode.READ_ONLY;
import static org.testcontainers.containers.BindMode.READ_WRITE;
import static org.testcontainers.containers.SelinuxContext.SHARED;

/**
* Tests for GenericContainerRules
Expand Down Expand Up @@ -101,6 +103,15 @@ public static void setupContent() throws FileNotFoundException {
.withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", READ_ONLY)
.withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done");

/**
* Map a file on the classpath to a file in the container, and then expose the content for testing.
*/
@ClassRule
public static GenericContainer alpineClasspathResourceSelinux = new GenericContainer("alpine:3.2")
.withExposedPorts(80)
.withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", READ_WRITE, SHARED)
.withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done");

/**
* Create a container with an extra host entry and expose the content of /etc/hosts for testing.
*/
Expand Down Expand Up @@ -203,6 +214,12 @@ public void customClasspathResourceMappingTest() throws IOException {
assertEquals("Resource on the classpath can be mapped using calls to withClasspathResourceMapping", "FOOBAR", line);
}

@Test
public void customClasspathResourceMappingWithSelinuxTest() throws IOException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not an SELinux expert, but maybe this might help?
https://www.cyberciti.biz/faq/howto-display-selinux-security-context-using-ls/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, we can get the labeled context on the container (provided we use a beefier image with SELinux utils installed), but I thought the :Z option used some host configs to label it as the correct type. Wouldn't this also require knowledge of some SELinux context on the host to make sure they match? Thought we'd want to avoid having to interrogate/put dependencies on the host.

String line = getReaderForContainerPort80(alpineClasspathResourceSelinux).readLine();
assertEquals("Resource on the classpath can be mapped using calls to withClasspathResourceMappingSelinux", "FOOBAR", line);
}

@Test
public void exceptionThrownWhenMappedPortNotFound() throws IOException {
assertThrows("When the requested port is not mapped, getMappedPort() throws an exception",
Expand Down