Skip to content

Commit e4b0865

Browse files
committed
[Security Manager Replacement] Phase off SecurityManager usage in favor of Java Agent
Signed-off-by: Andriy Redko <[email protected]>
1 parent 7b6108b commit e4b0865

File tree

32 files changed

+198
-242
lines changed

32 files changed

+198
-242
lines changed

build.gradle

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -433,11 +433,12 @@ gradle.projectsEvaluated {
433433

434434
project.tasks.withType(Test) { task ->
435435
if (task != null) {
436-
if (BuildParams.runtimeJavaVersion > JavaVersion.VERSION_17) {
437-
task.jvmArgs += ["-Djava.security.manager=allow"]
438-
}
439-
if (BuildParams.runtimeJavaVersion >= JavaVersion.VERSION_20) {
440-
task.jvmArgs += ["--add-modules=jdk.incubator.vector"]
436+
task.jvmArgs += ["--add-modules=jdk.incubator.vector"]
437+
438+
// Add Java Agent for security sandboxing
439+
if (!(project.path in [':build-tools', ":libs:agent-sm:bootstrap", ":libs:agent-sm:agent"])) {
440+
dependsOn(project(':libs:agent-sm:agent').prepareAgent)
441+
jvmArgs += ["-javaagent:" + project(':libs:agent-sm:agent').jar.archiveFile.get()]
441442
}
442443
}
443444
}

buildSrc/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,12 @@ dependencies {
110110
api 'com.netflix.nebula:gradle-info-plugin:12.1.6'
111111
api 'org.apache.rat:apache-rat:0.15'
112112
api "commons-io:commons-io:${props.getProperty('commonsio')}"
113-
api "net.java.dev.jna:jna:5.14.0"
113+
api "net.java.dev.jna:jna:5.16.0"
114114
api 'com.gradleup.shadow:shadow-gradle-plugin:8.3.5'
115115
api 'org.jdom:jdom2:2.0.6.1'
116116
api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${props.getProperty('kotlin')}"
117117
api 'de.thetaphi:forbiddenapis:3.8'
118-
api 'com.avast.gradle:gradle-docker-compose-plugin:0.17.6'
118+
api 'com.avast.gradle:gradle-docker-compose-plugin:0.17.12'
119119
api "org.yaml:snakeyaml:${props.getProperty('snakeyaml')}"
120120
api 'org.apache.maven:maven-model:3.9.6'
121121
api 'com.networknt:json-schema-validator:1.2.0'

buildSrc/src/main/java/org/opensearch/gradle/OpenSearchTestBasePlugin.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,6 @@ public void execute(Task t) {
115115
test.jvmArgs("--illegal-access=warn");
116116
}
117117
}
118-
if (test.getJavaVersion().compareTo(JavaVersion.VERSION_17) > 0) {
119-
test.jvmArgs("-Djava.security.manager=allow");
120-
}
121118
}
122119
});
123120
test.getJvmArgumentProviders().add(nonInputProperties);

client/rest-high-level/src/test/resources/org/opensearch/bootstrap/test.policy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@
88

99
grant {
1010
permission java.net.SocketPermission "*", "connect,resolve";
11+
permission java.net.NetPermission "accessUnixDomainSocket";
1112
};

distribution/archives/build.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ CopySpec archiveFiles(CopySpec modulesFiles, String distributionType, String pla
3838
into('lib') {
3939
with libFiles()
4040
}
41+
into('agent') {
42+
with agentFiles()
43+
}
4144
into('config') {
4245
dirPermissions {
4346
unix 0750
@@ -226,3 +229,9 @@ subprojects {
226229

227230
group = "org.opensearch.distribution"
228231
}
232+
233+
tasks.each {
234+
if (it.name.startsWith("build")) {
235+
it.dependsOn project(':libs:agent-sm:agent').assemble
236+
}
237+
}

distribution/build.gradle

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,18 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
357357
}
358358
}
359359

360+
agentFiles = {
361+
copySpec {
362+
from(project(':libs:agent-sm:agent').prepareAgent) {
363+
include '**/*.jar'
364+
exclude '**/*-javadoc.jar'
365+
exclude '**/*-sources.jar'
366+
// strip the version since jvm.options is using agent without version
367+
rename("opensearch-agent-${project.version}.jar", "opensearch-agent.jar")
368+
}
369+
}
370+
}
371+
360372
modulesFiles = { platform ->
361373
copySpec {
362374
eachFile {

distribution/src/config/jvm.options

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,6 @@ ${error.file}
7676
# JDK 9+ GC logging
7777
9-:-Xlog:gc*,gc+age=trace,safepoint:file=${loggc}:utctime,pid,tags:filecount=32,filesize=64m
7878

79-
# Explicitly allow security manager (https://bugs.openjdk.java.net/browse/JDK-8270380)
80-
18-:-Djava.security.manager=allow
81-
8279
# JDK 20+ Incubating Vector Module for SIMD optimizations;
8380
# disabling may reduce performance on vector optimized lucene
8481
20-:--add-modules=jdk.incubator.vector
@@ -89,3 +86,5 @@ ${error.file}
8986
# See please https://bugs.openjdk.org/browse/JDK-8341127 (openjdk/jdk#21283)
9087
23:-XX:CompileCommand=dontinline,java/lang/invoke/MethodHandle.setAsTypeCache
9188
23:-XX:CompileCommand=dontinline,java/lang/invoke/MethodHandle.asTypeUncached
89+
90+
21-:-javaagent:agent/opensearch-agent.jar

distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/SystemJvmOptions.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,21 +77,11 @@ static List<String> systemJvmOptions() {
7777
// log4j 2
7878
"-Dlog4j.shutdownHookEnabled=false",
7979
"-Dlog4j2.disable.jmx=true",
80-
// security manager
81-
allowSecurityManagerOption(),
8280
javaLocaleProviders()
8381
)
8482
).stream().filter(e -> e.isEmpty() == false).collect(Collectors.toList());
8583
}
8684

87-
private static String allowSecurityManagerOption() {
88-
if (Runtime.version().feature() > 17) {
89-
return "-Djava.security.manager=allow";
90-
} else {
91-
return "";
92-
}
93-
}
94-
9585
private static String maybeShowCodeDetailsInExceptionMessages() {
9686
if (Runtime.version().feature() >= 14) {
9787
return "-XX:+ShowCodeDetailsInExceptionMessages";

gradle/ide.gradle

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,7 @@ if (System.getProperty('idea.active') == 'true') {
8282
runConfigurations {
8383
defaults(JUnit) {
8484
vmParameters = '-ea -Djava.locale.providers=SPI,CLDR'
85-
if (BuildParams.runtimeJavaVersion > JavaVersion.VERSION_17) {
86-
vmParameters += ' -Djava.security.manager=allow'
87-
}
85+
vmParameters += ' -javaagent:' + project(':libs:agent-sm:agent').jar.archiveFile.get()
8886
}
8987
}
9088
copyright {

libs/agent-sm/agent/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,7 @@ tasks.test {
7575
tasks.check {
7676
dependsOn test
7777
}
78+
79+
tasks.named('assemble') {
80+
dependsOn prepareAgent
81+
}

libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/Agent.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ private static AgentBuilder createAgentBuilder(Instrumentation inst) throws Exce
101101
final AgentBuilder agentBuilder = new AgentBuilder.Default(byteBuddy).with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
102102
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
103103
.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
104-
.ignore(ElementMatchers.none())
104+
.ignore(ElementMatchers.nameContains("$MockitoMock$")) /* ingore all Mockito mocks */
105105
.type(systemType)
106106
.transform(socketTransformer)
107107
.type(pathType.or(fileChannelType))

libs/build.gradle

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,21 @@ subprojects {
4141
*/
4242
project.afterEvaluate {
4343
if (!project.path.equals(':libs:agent-sm:agent')) {
44-
configurations.all { Configuration conf ->
45-
dependencies.matching { it instanceof ProjectDependency }.all { ProjectDependency dep ->
46-
Project depProject = project.project(dep.path)
47-
if (depProject != null
48-
&& (false == depProject.path.equals(':libs:opensearch-core') &&
49-
false == depProject.path.equals(':libs:opensearch-common'))
50-
&& depProject.path.startsWith(':libs')) {
51-
throw new InvalidUserDataException("projects in :libs "
52-
+ "may not depend on other projects libs except "
53-
+ ":libs:opensearch-core or :libs:opensearch-common but "
54-
+ "${project.path} depends on ${depProject.path}")
44+
configurations.all { Configuration conf ->
45+
dependencies.matching { it instanceof ProjectDependency }.all { ProjectDependency dep ->
46+
Project depProject = project.project(dep.path)
47+
if (depProject != null
48+
&& (false == depProject.path.equals(':libs:opensearch-core') &&
49+
false == depProject.path.equals(':libs:opensearch-common')&&
50+
false == depProject.path.equals(':libs:agent-sm:agent-policy'))
51+
&& depProject.path.startsWith(':libs')) {
52+
throw new InvalidUserDataException("projects in :libs "
53+
+ "may not depend on other projects libs except "
54+
+ ":libs:opensearch-core or :libs:opensearch-common but "
55+
+ "${project.path} depends on ${depProject.path}")
56+
}
5557
}
5658
}
57-
}
5859
}
5960
}
6061
}

libs/secure-sm/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ apply plugin: 'opensearch.publish'
3131

3232
dependencies {
3333
// do not add non-test compile dependencies to secure-sm without a good reason to do so
34+
api project(":libs:agent-sm:agent-policy")
3435

3536
testImplementation "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}"
3637
testImplementation "junit:junit:${versions.junit}"

libs/secure-sm/src/main/java/org/opensearch/secure_sm/SecureSM.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ private boolean hasTrustedCallerChain() {
147147
};
148148
}
149149

150-
static final String[] TEST_RUNNER_PACKAGES = new String[] {
150+
public static final String[] TEST_RUNNER_PACKAGES = new String[] {
151+
// gradle worker
152+
"worker\\.org\\.gradle\\.process\\.internal\\.worker\\.GradleWorkerMain*",
151153
// surefire test runner
152154
"org\\.apache\\.maven\\.surefire\\.booter\\..*",
153155
// junit4 test runner

plugins/repository-hdfs/build.gradle

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,6 @@ for (String fixtureName : ['hdfsFixture', 'haHdfsFixture', 'secureHdfsFixture',
146146
}
147147
final List<String> miniHDFSArgs = []
148148

149-
if (BuildParams.runtimeJavaVersion >= JavaVersion.VERSION_23) {
150-
miniHDFSArgs.add('-Djava.security.manager=allow')
151-
}
152-
153149
// If it's a secure fixture, then depend on Kerberos Fixture and principals + add the krb5conf to the JVM options
154150
if (fixtureName.equals('secureHdfsFixture') || fixtureName.equals('secureHaHdfsFixture')) {
155151
miniHDFSArgs.add("-Djava.security.krb5.conf=${project(':test:fixtures:krb5kdc-fixture').ext.krb5Conf("hdfs")}");
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
grant {
10+
permission java.net.NetPermission "accessUnixDomainSocket";
11+
permission java.net.SocketPermission "*", "connect,resolve";
12+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
grant {
10+
permission java.net.NetPermission "accessUnixDomainSocket";
11+
permission java.net.SocketPermission "*", "connect,resolve";
12+
};

server/build.gradle

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ dependencies {
7171
api project(":libs:opensearch-task-commons")
7272
implementation project(':libs:opensearch-arrow-spi')
7373

74+
compileOnly project(":libs:agent-sm:bootstrap")
7475
compileOnly project(':libs:opensearch-plugin-classloader')
7576
testRuntimeOnly project(':libs:opensearch-plugin-classloader')
7677

@@ -377,9 +378,7 @@ tasks.named("licenseHeaders").configure {
377378

378379
tasks.test {
379380
environment "node.roles.test", "[]"
380-
if (BuildParams.runtimeJavaVersion > JavaVersion.VERSION_1_8) {
381-
jvmArgs += ["--add-opens", "java.base/java.nio.file=ALL-UNNAMED"]
382-
}
381+
jvmArgs += ["--add-opens", "java.base/java.nio.file=ALL-UNNAMED", "-Djdk.attach.allowAttachSelf=true", "-XX:+EnableDynamicAgentLoading" ]
383382
}
384383

385384
tasks.named("sourcesJar").configure {

server/src/main/java/org/opensearch/bootstrap/BootstrapChecks.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.opensearch.discovery.DiscoveryModule;
4848
import org.opensearch.env.Environment;
4949
import org.opensearch.index.IndexModule;
50+
import org.opensearch.javaagent.bootstrap.AgentPolicy;
5051
import org.opensearch.monitor.jvm.JvmInfo;
5152
import org.opensearch.monitor.process.ProcessProbe;
5253
import org.opensearch.node.NodeRoleSettings;
@@ -57,6 +58,7 @@
5758
import java.nio.file.Files;
5859
import java.nio.file.Path;
5960
import java.security.AllPermission;
61+
import java.security.Policy;
6062
import java.util.ArrayList;
6163
import java.util.Arrays;
6264
import java.util.Collections;
@@ -720,10 +722,10 @@ public final BootstrapCheckResult check(BootstrapContext context) {
720722

721723
@SuppressWarnings("removal")
722724
boolean isAllPermissionGranted() {
723-
final SecurityManager sm = System.getSecurityManager();
724-
assert sm != null;
725+
final Policy policy = AgentPolicy.getPolicy();
726+
assert policy != null;
725727
try {
726-
sm.checkPermission(new AllPermission());
728+
AgentPolicy.checkPermission(new AllPermission());
727729
} catch (final SecurityException e) {
728730
return false;
729731
}

server/src/main/java/org/opensearch/bootstrap/OpenSearch.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848

4949
import java.io.IOException;
5050
import java.nio.file.Path;
51-
import java.security.Permission;
5251
import java.security.Security;
5352
import java.util.Arrays;
5453
import java.util.Locale;
@@ -86,19 +85,7 @@ class OpenSearch extends EnvironmentAwareCommand {
8685
@SuppressWarnings("removal")
8786
public static void main(final String[] args) throws Exception {
8887
overrideDnsCachePolicyProperties();
89-
/*
90-
* We want the JVM to think there is a security manager installed so that if internal policy decisions that would be based on the
91-
* presence of a security manager or lack thereof act as if there is a security manager present (e.g., DNS cache policy). This
92-
* forces such policies to take effect immediately.
93-
*/
94-
System.setSecurityManager(new SecurityManager() {
95-
96-
@Override
97-
public void checkPermission(Permission perm) {
98-
// grant all permissions so that we can later set the security manager to the one that we want
99-
}
10088

101-
});
10289
LogConfigurator.registerErrorListener();
10390
final OpenSearch opensearch = new OpenSearch();
10491
int status = main(args, opensearch, Terminal.DEFAULT);

server/src/main/java/org/opensearch/bootstrap/Security.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@
4141
import org.opensearch.common.transport.PortsRange;
4242
import org.opensearch.env.Environment;
4343
import org.opensearch.http.HttpTransportSettings;
44+
import org.opensearch.javaagent.bootstrap.AgentPolicy;
4445
import org.opensearch.plugins.PluginInfo;
4546
import org.opensearch.plugins.PluginsService;
46-
import org.opensearch.secure_sm.SecureSM;
47+
import org.opensearch.secure_sm.policy.PolicyFile;
4748
import org.opensearch.transport.TcpTransport;
4849

4950
import java.io.IOException;
@@ -59,7 +60,6 @@
5960
import java.security.NoSuchAlgorithmException;
6061
import java.security.Permissions;
6162
import java.security.Policy;
62-
import java.security.URIParameter;
6363
import java.util.ArrayList;
6464
import java.util.Collections;
6565
import java.util.HashMap;
@@ -144,23 +144,26 @@ static void configure(Environment environment, boolean filterBadDefaults) throws
144144

145145
// enable security policy: union of template and environment-based paths, and possibly plugin permissions
146146
Map<String, URL> codebases = getCodebaseJarMap(JarHell.parseClassPath());
147-
Policy.setPolicy(
147+
148+
// enable security manager
149+
final String[] classesThatCanExit = new String[] {
150+
// SecureSM matches class names as regular expressions so we escape the $ that arises from the nested class name
151+
OpenSearchUncaughtExceptionHandler.PrivilegedHaltAction.class.getName().replace("$", "\\$"),
152+
Command.class.getName() };
153+
154+
AgentPolicy.setPolicy(
148155
new OpenSearchPolicy(
149156
codebases,
150157
createPermissions(environment),
151158
getPluginPermissions(environment),
152159
filterBadDefaults,
153160
createRecursiveDataPathPermission(environment)
154-
)
161+
),
162+
Set.of() /* trusted hosts */,
163+
Set.of() /* trusted file systems */,
164+
new AgentPolicy.AnyCanExit(classesThatCanExit)
155165
);
156166

157-
// enable security manager
158-
final String[] classesThatCanExit = new String[] {
159-
// SecureSM matches class names as regular expressions so we escape the $ that arises from the nested class name
160-
OpenSearchUncaughtExceptionHandler.PrivilegedHaltAction.class.getName().replace("$", "\\$"),
161-
Command.class.getName() };
162-
System.setSecurityManager(new SecureSM(classesThatCanExit));
163-
164167
// do some basic tests
165168
selfTest();
166169
}
@@ -280,14 +283,14 @@ static Policy readPolicy(URL policyFile, Map<String, URL> codebases) {
280283
addCodebaseToSystemProperties(propertiesSet, url, property, aliasProperty);
281284
}
282285

283-
return Policy.getInstance("JavaPolicy", new URIParameter(policyFile.toURI()));
286+
return new PolicyFile(policyFile);
284287
} finally {
285288
// clear codebase properties
286289
for (String property : propertiesSet) {
287290
System.clearProperty(property);
288291
}
289292
}
290-
} catch (NoSuchAlgorithmException | URISyntaxException e) {
293+
} catch (final RuntimeException e) {
291294
throw new IllegalArgumentException("unable to parse policy file `" + policyFile + "`", e);
292295
}
293296
}

0 commit comments

Comments
 (0)